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 ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -131,7 +131,7 @@ const InvoiceInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='invoice'
|
type='invoice'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import {
|
import {
|
||||||
getModelProperty,
|
getModelProperty,
|
||||||
@ -121,7 +121,7 @@ const PaymentPolicyInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='paymentPolicy'
|
type='paymentPolicy'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -121,7 +121,7 @@ const PaymentInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='payment'
|
type='payment'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -112,7 +112,7 @@ const TaxRecordInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='taxRecord'
|
type='taxRecord'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import NoteIcon from '../../../Icons/NoteIcon'
|
|||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||||
import GraphIcon from '../../../Icons/GraphIcon'
|
import GraphIcon from '../../../Icons/GraphIcon'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle'
|
import UserNotifierToggle from '../../common/UserNotifierToggle'
|
||||||
import ScrollBox from '../../common/ScrollBox'
|
import ScrollBox from '../../common/ScrollBox'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -116,7 +116,7 @@ const FilamentStockInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='filamentStock'
|
type='filamentStock'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
|
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
@ -115,6 +116,11 @@ const OrderItemInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
|
<InfoActionButtons
|
||||||
|
type='orderItem'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
<ActivityIndicator
|
<ActivityIndicator
|
||||||
activities={objectFormState.activities}
|
activities={objectFormState.activities}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
||||||
@ -123,7 +123,7 @@ const PartStockInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='partStock'
|
type='partStock'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
||||||
@ -140,7 +140,7 @@ const ProductStockInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='productStock'
|
type='productStock'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||||
@ -143,7 +143,7 @@ const PurchaseOrderInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='purchaseOrder'
|
type='purchaseOrder'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||||
import NewOrderItem from '../OrderItems/NewOrderItem.jsx'
|
import NewOrderItem from '../OrderItems/NewOrderItem.jsx'
|
||||||
@ -146,7 +146,7 @@ const PurchaseOrderInfo = () => {
|
|||||||
visibleState={collapseState}
|
visibleState={collapseState}
|
||||||
updateVisibleState={updateCollapseState}
|
updateVisibleState={updateCollapseState}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='purchaseOrder'
|
type='purchaseOrder'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -124,7 +124,7 @@ const ShipmentInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='shipment'
|
type='shipment'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -122,7 +122,7 @@ const StockAuditInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='stockAudit'
|
type='stockAudit'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -130,7 +130,7 @@ const StockLocationInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='stockLocation'
|
type='stockLocation'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ const StockTransferInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='stockTransfer'
|
type='stockTransfer'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -113,7 +113,7 @@ const AppPasswordInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='appPassword'
|
type='appPassword'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -118,7 +118,7 @@ const CourierServiceInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='courierService'
|
type='courierService'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -110,7 +110,7 @@ const CourierInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='courier'
|
type='courier'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -114,7 +114,7 @@ const DocumentJobInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='documentJob'
|
type='documentJob'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -122,7 +122,7 @@ const DocumentPrinterInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='documentPrinter'
|
type='documentPrinter'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -114,7 +114,7 @@ const DocumentSizeInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='documentSize'
|
type='documentSize'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -119,7 +119,7 @@ const DocumentTemplateInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='documentTemplate'
|
type='documentTemplate'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
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 { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -109,7 +109,7 @@ const FilamentSkuInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
|||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import FilamentIcon from '../../../Icons/FilamentIcon.jsx'
|
import FilamentIcon from '../../../Icons/FilamentIcon.jsx'
|
||||||
import FilamentSkuIcon from '../../../Icons/FilamentSkuIcon.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 ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
@ -131,7 +131,7 @@ const FilamentInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='filament'
|
type='filament'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import FileIcon from '../../../Icons/FileIcon.jsx'
|
import FileIcon from '../../../Icons/FileIcon.jsx'
|
||||||
import FilePreview from '../../common/FilePreview.jsx'
|
import FilePreview from '../../common/FilePreview.jsx'
|
||||||
@ -124,7 +124,7 @@ const FileInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='file'
|
type='file'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
||||||
import DocumentPrinterIcon from '../../../Icons/DocumentPrinterIcon.jsx'
|
import DocumentPrinterIcon from '../../../Icons/DocumentPrinterIcon.jsx'
|
||||||
@ -126,7 +126,7 @@ const HostInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='host'
|
type='host'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -112,7 +112,7 @@ const MaterialInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='material'
|
type='material'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -103,7 +103,7 @@ const NoteTypeInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='noteType'
|
type='noteType'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -113,7 +113,7 @@ const NoteInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='note'
|
type='note'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -104,7 +104,7 @@ const PartSkuInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='partSku'
|
type='partSku'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -116,7 +116,7 @@ const PartInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='part'
|
type='part'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import {
|
import {
|
||||||
getModelByName,
|
getModelByName,
|
||||||
@ -115,7 +115,7 @@ const PermissionSettingInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='permissionSetting'
|
type='permissionSetting'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -113,7 +113,7 @@ const ProductCategoryInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='productCategory'
|
type='productCategory'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
@ -111,7 +111,7 @@ const ProductSkuInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='productSku'
|
type='productSku'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { useActions } from '../../context/ActionsContext'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -116,7 +116,7 @@ const ProductInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='product'
|
type='product'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -110,7 +110,7 @@ const StockAuditLevelInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='stockAuditLevel'
|
type='stockAuditLevel'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -113,7 +113,7 @@ const TaxRateInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='taxRate'
|
type='taxRate'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
@ -115,7 +115,7 @@ const UserGroupInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='userGroup'
|
type='userGroup'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
@ -114,7 +114,7 @@ const UserInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='user'
|
type='user'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -109,7 +109,7 @@ const VendorInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='vendor'
|
type='vendor'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Card, Flex, Space, Spin } from 'antd'
|
|||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import ActionHandler from '../../common/ActionHandler'
|
import ActionHandler from '../../common/ActionHandler'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import EditButtons from '../../common/EditButtons'
|
||||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
import FilamentIcon from '../../../Icons/FilamentIcon'
|
import FilamentIcon from '../../../Icons/FilamentIcon'
|
||||||
@ -158,7 +158,7 @@ const FilamentProfileInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='filamentProfile'
|
type='filamentProfile'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -120,7 +120,7 @@ const GCodeFileInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='gcodeFile'
|
type='gcodeFile'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import JobIcon from '../../../Icons/JobIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -126,7 +126,7 @@ const JobInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='job'
|
type='job'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Card, Flex, Space, Spin } from 'antd'
|
|||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import ActionHandler from '../../common/ActionHandler'
|
import ActionHandler from '../../common/ActionHandler'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import EditButtons from '../../common/EditButtons'
|
||||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
import HotEndIcon from '../../../Icons/HotEndIcon'
|
import HotEndIcon from '../../../Icons/HotEndIcon'
|
||||||
@ -130,7 +130,7 @@ const PrinterProfileInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='printerProfile'
|
type='printerProfile'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
|
import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
|
||||||
@ -127,7 +127,7 @@ const PrinterInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='printer'
|
type='printer'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
@ -104,7 +104,7 @@ const SubJobInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='subJob'
|
type='subJob'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
@ -109,7 +109,7 @@ const ClientInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='client'
|
type='client'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import {
|
import {
|
||||||
getModelProperty,
|
getModelProperty,
|
||||||
@ -121,7 +121,7 @@ const FulfillmentPolicyInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='fulfillmentPolicy'
|
type='fulfillmentPolicy'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import {
|
import {
|
||||||
@ -132,7 +132,7 @@ const ListingVarientInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='listingVarient'
|
type='listingVarient'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import {
|
import {
|
||||||
getModelByName,
|
getModelByName,
|
||||||
@ -131,7 +131,7 @@ const ListingInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='listing'
|
type='listing'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
||||||
@ -189,7 +189,7 @@ const MarketplaceInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='marketplace'
|
type='marketplace'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
|||||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import {
|
import {
|
||||||
getModelProperty,
|
getModelProperty,
|
||||||
@ -121,7 +121,7 @@ const ReturnPolicyInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='returnPolicy'
|
type='returnPolicy'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||||
@ -141,7 +141,7 @@ const SalesOrderInfo = () => {
|
|||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
/>
|
/>
|
||||||
<DocumentPrintButton
|
<InfoActionButtons
|
||||||
type='salesOrder'
|
type='salesOrder'
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
disabled={objectFormState.loading}
|
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':
|
case 'text':
|
||||||
return <Input placeholder={label} {...inputProps} />
|
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':
|
case 'codeBlock':
|
||||||
return (
|
return (
|
||||||
<CodeBlockEditor
|
<CodeBlockEditor
|
||||||
|
|||||||
@ -96,6 +96,11 @@ const parseEjsErrorLines = (message) => {
|
|||||||
|
|
||||||
const TemplateEditor = ({
|
const TemplateEditor = ({
|
||||||
objectData,
|
objectData,
|
||||||
|
template,
|
||||||
|
templateType = 'documentTemplate',
|
||||||
|
editorLanguage = 'fcTemplateLang',
|
||||||
|
subjectProperty,
|
||||||
|
previewCapabilities,
|
||||||
loading,
|
loading,
|
||||||
collapseState,
|
collapseState,
|
||||||
isEditing,
|
isEditing,
|
||||||
@ -104,6 +109,7 @@ const TemplateEditor = ({
|
|||||||
setObjectData
|
setObjectData
|
||||||
}) => {
|
}) => {
|
||||||
const { fetchTemplateFormat } = useContext(ApiServerContext)
|
const { fetchTemplateFormat } = useContext(ApiServerContext)
|
||||||
|
const currentTemplate = template || objectData
|
||||||
const [testObjectOpen, setTestObjectOpen] = useState(false)
|
const [testObjectOpen, setTestObjectOpen] = useState(false)
|
||||||
const [testObjectViewMode, setTestObjectViewMode] = useState('Tree')
|
const [testObjectViewMode, setTestObjectViewMode] = useState('Tree')
|
||||||
const [previewMessage, setPreviewMessage] = useState('No issues found.')
|
const [previewMessage, setPreviewMessage] = useState('No issues found.')
|
||||||
@ -150,14 +156,14 @@ const TemplateEditor = ({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleFormatCode = useCallback(async () => {
|
const handleFormatCode = useCallback(async () => {
|
||||||
const content = form?.getFieldValue?.('content') ?? objectData?.content
|
const content = form?.getFieldValue?.('content') ?? currentTemplate?.content
|
||||||
if (content == null || typeof content !== 'string') {
|
if (content == null || typeof content !== 'string') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormatLoading(true)
|
setFormatLoading(true)
|
||||||
try {
|
try {
|
||||||
const result = await fetchTemplateFormat(content)
|
const result = await fetchTemplateFormat(content, null, templateType)
|
||||||
if (result?.error) {
|
if (result?.error) {
|
||||||
handlePreviewMessage(result.error, true)
|
handlePreviewMessage(result.error, true)
|
||||||
return
|
return
|
||||||
@ -177,10 +183,11 @@ const TemplateEditor = ({
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
form,
|
form,
|
||||||
objectData?.content,
|
currentTemplate?.content,
|
||||||
fetchTemplateFormat,
|
fetchTemplateFormat,
|
||||||
handlePreviewMessage,
|
handlePreviewMessage,
|
||||||
setObjectData
|
setObjectData,
|
||||||
|
templateType
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -195,8 +202,10 @@ const TemplateEditor = ({
|
|||||||
styles={{ body: { height: '100%', padding: '22px' } }}
|
styles={{ body: { height: '100%', padding: '22px' } }}
|
||||||
>
|
>
|
||||||
<TemplatePreview
|
<TemplatePreview
|
||||||
objectData={objectData?.testObject}
|
objectData={currentTemplate?.testObject}
|
||||||
documentTemplate={objectData}
|
template={currentTemplate}
|
||||||
|
templateType={templateType}
|
||||||
|
capabilities={previewCapabilities}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
style={style}
|
style={style}
|
||||||
@ -244,6 +253,24 @@ const TemplateEditor = ({
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</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
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -264,8 +291,8 @@ const TemplateEditor = ({
|
|||||||
name={'content'}
|
name={'content'}
|
||||||
height='100%'
|
height='100%'
|
||||||
type='codeBlock'
|
type='codeBlock'
|
||||||
language={'fcTemplateLang'}
|
language={editorLanguage}
|
||||||
objectData={objectData}
|
objectData={currentTemplate}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
form?.setFieldValue?.('content', value)
|
form?.setFieldValue?.('content', value)
|
||||||
setObjectData?.((prev) => ({
|
setObjectData?.((prev) => ({
|
||||||
@ -274,7 +301,7 @@ const TemplateEditor = ({
|
|||||||
}))
|
}))
|
||||||
}}
|
}}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
autoCompleteObject={objectData?.testObject}
|
autoCompleteObject={currentTemplate?.testObject}
|
||||||
errorLines={errorLines}
|
errorLines={errorLines}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -318,13 +345,13 @@ const TemplateEditor = ({
|
|||||||
type={'codeBlock'}
|
type={'codeBlock'}
|
||||||
name='testObject'
|
name='testObject'
|
||||||
language='json'
|
language='json'
|
||||||
objectData={objectData}
|
objectData={currentTemplate}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{testObjectViewMode == 'Tree' && (
|
{testObjectViewMode == 'Tree' && (
|
||||||
<DataTree
|
<DataTree
|
||||||
data={objectData?.testObject}
|
data={currentTemplate?.testObject}
|
||||||
defaultExpandAll={true}
|
defaultExpandAll={true}
|
||||||
showValueCopy={false}
|
showValueCopy={false}
|
||||||
showKeyCopy={true}
|
showKeyCopy={true}
|
||||||
@ -340,6 +367,11 @@ const TemplateEditor = ({
|
|||||||
TemplateEditor.propTypes = {
|
TemplateEditor.propTypes = {
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
|
template: PropTypes.object,
|
||||||
|
templateType: PropTypes.string,
|
||||||
|
editorLanguage: PropTypes.string,
|
||||||
|
subjectProperty: PropTypes.string,
|
||||||
|
previewCapabilities: PropTypes.object,
|
||||||
collapseState: PropTypes.object,
|
collapseState: PropTypes.object,
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
useCallback
|
useCallback
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
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 PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||||
@ -24,6 +24,10 @@ const noop = () => {}
|
|||||||
const TemplatePreview = ({
|
const TemplatePreview = ({
|
||||||
objectData,
|
objectData,
|
||||||
documentTemplate,
|
documentTemplate,
|
||||||
|
content = null,
|
||||||
|
template,
|
||||||
|
templateType = 'documentTemplate',
|
||||||
|
capabilities = {},
|
||||||
loading,
|
loading,
|
||||||
isEditing,
|
isEditing,
|
||||||
onTestObjectOpen = noop,
|
onTestObjectOpen = noop,
|
||||||
@ -31,6 +35,10 @@ const TemplatePreview = ({
|
|||||||
showTestObject = false,
|
showTestObject = false,
|
||||||
showPreviewSwitch = true
|
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 iframeRef = useRef(null)
|
||||||
const scrollElementRef = useRef(null)
|
const scrollElementRef = useRef(null)
|
||||||
const savedScrollRef = useRef({ top: 0, left: 0 })
|
const savedScrollRef = useRef({ top: 0, left: 0 })
|
||||||
@ -48,6 +56,7 @@ const TemplatePreview = ({
|
|||||||
const [downloadProgress, setDownloadProgress] = useState(null)
|
const [downloadProgress, setDownloadProgress] = useState(null)
|
||||||
const [previewScale, setPreviewScale] = useState(1)
|
const [previewScale, setPreviewScale] = useState(1)
|
||||||
const [previewType, setPreviewType] = useState('HTML')
|
const [previewType, setPreviewType] = useState('HTML')
|
||||||
|
const [previewWidth, setPreviewWidth] = useState(defaultPreviewWidth)
|
||||||
const [panMode, setPanMode] = useState(false)
|
const [panMode, setPanMode] = useState(false)
|
||||||
const [isPanning, setIsPanning] = useState(false)
|
const [isPanning, setIsPanning] = useState(false)
|
||||||
const panStartRef = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 })
|
const panStartRef = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 })
|
||||||
@ -121,8 +130,12 @@ const TemplatePreview = ({
|
|||||||
parseFloat(containerStyle.paddingBottom)
|
parseFloat(containerStyle.paddingBottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentWidth = Math.ceil(visual.width + padX)
|
const contentWidth = showWidthControl
|
||||||
const contentHeight = Math.ceil(visual.height + padY)
|
? 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]')) {
|
if (!doc.head?.querySelector('style[data-preview-outer-scroll]')) {
|
||||||
const styleEl = doc.createElement('style')
|
const styleEl = doc.createElement('style')
|
||||||
@ -140,7 +153,7 @@ const TemplatePreview = ({
|
|||||||
}
|
}
|
||||||
return { width, height }
|
return { width, height }
|
||||||
})
|
})
|
||||||
}, [])
|
}, [previewWidth, showWidthControl])
|
||||||
|
|
||||||
const restoreScrollPosition = useCallback(() => {
|
const restoreScrollPosition = useCallback(() => {
|
||||||
const scrollEl = scrollElementRef.current
|
const scrollEl = scrollElementRef.current
|
||||||
@ -200,8 +213,11 @@ const TemplatePreview = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPreviewContentHTML('')
|
setPreviewContentHTML('')
|
||||||
setIframeSize({ width: '100%', height: 'auto' })
|
setIframeSize({
|
||||||
}, [])
|
width: showWidthControl ? `${previewWidth}px` : '100%',
|
||||||
|
height: 'auto'
|
||||||
|
})
|
||||||
|
}, [previewWidth, showWidthControl])
|
||||||
|
|
||||||
const stopRenderProgressListener = useCallback(() => {
|
const stopRenderProgressListener = useCallback(() => {
|
||||||
if (typeof renderUnsubscribeRef.current === 'function') {
|
if (typeof renderUnsubscribeRef.current === 'function') {
|
||||||
@ -234,7 +250,7 @@ const TemplatePreview = ({
|
|||||||
|
|
||||||
const reloadPreviewPDF = useCallback(
|
const reloadPreviewPDF = useCallback(
|
||||||
(content, testObject = {}) => {
|
(content, testObject = {}) => {
|
||||||
const templateId = documentTemplate?._id
|
const templateId = currentTemplate?._id
|
||||||
if (!templateId) {
|
if (!templateId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -346,7 +362,7 @@ const TemplatePreview = ({
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
applyPdfResult,
|
applyPdfResult,
|
||||||
documentTemplate?._id,
|
currentTemplate?._id,
|
||||||
fetchTemplateDownload,
|
fetchTemplateDownload,
|
||||||
stopRenderProgressListener,
|
stopRenderProgressListener,
|
||||||
subscribeToObjectEvent
|
subscribeToObjectEvent
|
||||||
@ -355,14 +371,19 @@ const TemplatePreview = ({
|
|||||||
|
|
||||||
const reloadPreview = useCallback(
|
const reloadPreview = useCallback(
|
||||||
(content, testObject = {}, scale = 1) => {
|
(content, testObject = {}, scale = 1) => {
|
||||||
const templateId = documentTemplate?._id
|
const templateId = currentTemplate?._id
|
||||||
if (!templateId) {
|
if (!templateId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestId = ++previewRequestIdRef.current
|
const requestId = ++previewRequestIdRef.current
|
||||||
setReloadLoading(true)
|
setReloadLoading(true)
|
||||||
fetchTemplatePreview(templateId, content, testObject, scale, (result) => {
|
fetchTemplatePreview(
|
||||||
|
templateId,
|
||||||
|
content,
|
||||||
|
testObject,
|
||||||
|
scale,
|
||||||
|
(result) => {
|
||||||
if (requestId !== previewRequestIdRef.current) {
|
if (requestId !== previewRequestIdRef.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -373,21 +394,38 @@ const TemplatePreview = ({
|
|||||||
updatePreviewContentHTML(result.html)
|
updatePreviewContentHTML(result.html)
|
||||||
onPreviewMessage('No issues found.', false)
|
onPreviewMessage('No issues found.', false)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
},
|
},
|
||||||
[fetchTemplatePreview, onPreviewMessage, documentTemplate?._id]
|
templateType,
|
||||||
|
showWidthControl ? { width: previewWidth } : {}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[
|
||||||
|
currentTemplate?._id,
|
||||||
|
fetchTemplatePreview,
|
||||||
|
onPreviewMessage,
|
||||||
|
previewWidth,
|
||||||
|
showWidthControl,
|
||||||
|
templateType
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const templateContent = documentTemplate?.content
|
const templateContent = currentTemplate?.content
|
||||||
const templateId = documentTemplate?._id
|
const templateId = currentTemplate?._id
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (content != null) {
|
||||||
|
updatePreviewContentHTML(content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!templateId || !templateContent || previewType != 'HTML') {
|
if (!templateId || !templateContent || previewType != 'HTML') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeoutId = window.setTimeout(() => {
|
const timeoutId = window.setTimeout(() => {
|
||||||
|
if (content == null) {
|
||||||
reloadPreview(templateContent, objectData, previewScale)
|
reloadPreview(templateContent, objectData, previewScale)
|
||||||
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -398,8 +436,10 @@ const TemplatePreview = ({
|
|||||||
templateContent,
|
templateContent,
|
||||||
templateId,
|
templateId,
|
||||||
previewScale,
|
previewScale,
|
||||||
|
previewWidth,
|
||||||
previewType,
|
previewType,
|
||||||
reloadPreview
|
reloadPreview,
|
||||||
|
content
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -534,7 +574,20 @@ const TemplatePreview = ({
|
|||||||
}}
|
}}
|
||||||
disabled={loading || reloadLoading}
|
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
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{ value: 'HTML', label: 'HTML' },
|
{ value: 'HTML', label: 'HTML' },
|
||||||
@ -561,13 +614,13 @@ const TemplatePreview = ({
|
|||||||
<Flex gap={'small'} ref={toolbarRef}>
|
<Flex gap={'small'} ref={toolbarRef}>
|
||||||
{showTestObject == true ? (
|
{showTestObject == true ? (
|
||||||
<>
|
<>
|
||||||
{documentTemplate?.objectType ? (
|
{currentTemplate?.objectType ? (
|
||||||
<ObjectProperty
|
<ObjectProperty
|
||||||
objectType={documentTemplate?.objectType}
|
objectType={currentTemplate?.objectType}
|
||||||
name={'testObject'}
|
name={'testObject'}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
objectData={documentTemplate}
|
objectData={currentTemplate}
|
||||||
disabled={!isEditing || documentTemplate?.global}
|
disabled={!isEditing || currentTemplate?.global}
|
||||||
type={'object'}
|
type={'object'}
|
||||||
style={{ minWidth: 0 }}
|
style={{ minWidth: 0 }}
|
||||||
/>
|
/>
|
||||||
@ -578,7 +631,7 @@ const TemplatePreview = ({
|
|||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
icon={<InfoCircleIcon />}
|
icon={<InfoCircleIcon />}
|
||||||
disabled={documentTemplate?.global}
|
disabled={currentTemplate?.global}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onTestObjectOpen()
|
onTestObjectOpen()
|
||||||
}}
|
}}
|
||||||
@ -707,12 +760,16 @@ TemplatePreview.propTypes = {
|
|||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
objectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
objectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||||
documentTemplate: PropTypes.object,
|
documentTemplate: PropTypes.object,
|
||||||
|
template: PropTypes.object,
|
||||||
|
templateType: PropTypes.string,
|
||||||
|
capabilities: PropTypes.object,
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
showTestObject: PropTypes.bool,
|
showTestObject: PropTypes.bool,
|
||||||
onTestObjectOpen: PropTypes.func,
|
onTestObjectOpen: PropTypes.func,
|
||||||
onPreviewMessage: PropTypes.func,
|
onPreviewMessage: PropTypes.func,
|
||||||
showPreviewSwitch: PropTypes.bool
|
showPreviewSwitch: PropTypes.bool,
|
||||||
|
content: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TemplatePreview
|
export default TemplatePreview
|
||||||
|
|||||||
@ -2105,11 +2105,15 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
URL.revokeObjectURL(fileUrl)
|
URL.revokeObjectURL(fileUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTemplateFormat = async (content, callback) => {
|
const fetchTemplateFormat = async (
|
||||||
|
content,
|
||||||
|
callback,
|
||||||
|
templateType = 'documentTemplate'
|
||||||
|
) => {
|
||||||
logger.debug('Formatting template content...')
|
logger.debug('Formatting template content...')
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${config.backendUrl}/documenttemplates/format`,
|
`${config.backendUrl}/${getObjectEndpoint(templateType)}/format`,
|
||||||
{ content },
|
{ content },
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@ -2137,17 +2141,20 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
content,
|
content,
|
||||||
testObject,
|
testObject,
|
||||||
scale,
|
scale,
|
||||||
callback
|
callback,
|
||||||
|
templateType = 'documentTemplate',
|
||||||
|
previewOptions = {}
|
||||||
) => {
|
) => {
|
||||||
logger.debug('Fetching preview...')
|
logger.debug('Fetching preview...')
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${config.backendUrl}/documenttemplates/${id}/preview`,
|
`${config.backendUrl}/${getObjectEndpoint(templateType)}/${id}/preview`,
|
||||||
{
|
{
|
||||||
content,
|
content,
|
||||||
testObject,
|
testObject,
|
||||||
scale,
|
scale,
|
||||||
padding: true
|
...(templateType === 'documentTemplate' ? { padding: true } : {}),
|
||||||
|
...previewOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@ -289,6 +289,18 @@ export function getStateTagInfo(state) {
|
|||||||
status = 'error'
|
status = 'error'
|
||||||
text = 'Overdue'
|
text = 'Overdue'
|
||||||
break
|
break
|
||||||
|
case 'rendering':
|
||||||
|
status = 'volcano'
|
||||||
|
text = 'Rendering'
|
||||||
|
break
|
||||||
|
case 'sending':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Sending'
|
||||||
|
break
|
||||||
|
case 'read':
|
||||||
|
status = 'success'
|
||||||
|
text = 'Read'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
status = 'default'
|
status = 'default'
|
||||||
text = state || 'Unknown'
|
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 PaymentPolicyIcon from './PaymentPolicyIcon'
|
||||||
import AppPasswordIcon from './AppPasswordIcon'
|
import AppPasswordIcon from './AppPasswordIcon'
|
||||||
import InfoCircleIcon from './InfoCircleIcon'
|
import InfoCircleIcon from './InfoCircleIcon'
|
||||||
|
import MailIcon from './MailIcon'
|
||||||
|
import EmailAccountIcon from './EmailAccountIcon'
|
||||||
|
import EmailTemplateIcon from './EmailTemplateIcon'
|
||||||
|
|
||||||
const toEmoji = (emoji) => <span aria-hidden>{emoji}</span>
|
const toEmoji = (emoji) => <span aria-hidden>{emoji}</span>
|
||||||
|
|
||||||
@ -121,6 +124,10 @@ const sidebarIconMap = {
|
|||||||
taxRate: <TaxRateIcon />,
|
taxRate: <TaxRateIcon />,
|
||||||
taxRecord: <TaxRecordIcon />,
|
taxRecord: <TaxRecordIcon />,
|
||||||
appPassword: <AppPasswordIcon />,
|
appPassword: <AppPasswordIcon />,
|
||||||
|
email: <MailIcon />,
|
||||||
|
emailAccount: <EmailAccountIcon />,
|
||||||
|
emailTemplate: <EmailTemplateIcon />,
|
||||||
|
emailMessage: <MailIcon />,
|
||||||
infoCircle: <InfoCircleIcon />,
|
infoCircle: <InfoCircleIcon />,
|
||||||
sessionStorage: toEmoji('🗃️'),
|
sessionStorage: toEmoji('🗃️'),
|
||||||
authDebug: toEmoji('🔐'),
|
authDebug: toEmoji('🔐'),
|
||||||
|
|||||||
@ -41,6 +41,9 @@ import { DocumentSize } from './models/DocumentSize.js'
|
|||||||
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
||||||
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
||||||
import { DocumentJob } from './models/DocumentJob.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 { TaxRate } from './models/TaxRate.js'
|
||||||
import { TaxRecord } from './models/TaxRecord.js'
|
import { TaxRecord } from './models/TaxRecord.js'
|
||||||
import { Invoice } from './models/Invoice.js'
|
import { Invoice } from './models/Invoice.js'
|
||||||
@ -99,6 +102,9 @@ export const objectModels = [
|
|||||||
DocumentTemplate,
|
DocumentTemplate,
|
||||||
DocumentPrinter,
|
DocumentPrinter,
|
||||||
DocumentJob,
|
DocumentJob,
|
||||||
|
EmailAccount,
|
||||||
|
EmailTemplate,
|
||||||
|
EmailMessage,
|
||||||
TaxRate,
|
TaxRate,
|
||||||
TaxRecord,
|
TaxRecord,
|
||||||
Invoice,
|
Invoice,
|
||||||
@ -158,6 +164,9 @@ export {
|
|||||||
DocumentTemplate,
|
DocumentTemplate,
|
||||||
DocumentPrinter,
|
DocumentPrinter,
|
||||||
DocumentJob,
|
DocumentJob,
|
||||||
|
EmailAccount,
|
||||||
|
EmailTemplate,
|
||||||
|
EmailMessage,
|
||||||
TaxRate,
|
TaxRate,
|
||||||
TaxRecord,
|
TaxRecord,
|
||||||
Invoice,
|
Invoice,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export const AuditLog = {
|
|||||||
filters: [
|
filters: [
|
||||||
'parent',
|
'parent',
|
||||||
'owner',
|
'owner',
|
||||||
|
'system',
|
||||||
'operation',
|
'operation',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'updatedAt',
|
'updatedAt',
|
||||||
@ -74,16 +75,23 @@ export const AuditLog = {
|
|||||||
{
|
{
|
||||||
name: 'owner',
|
name: 'owner',
|
||||||
label: 'Owner',
|
label: 'Owner',
|
||||||
type: 'object',
|
type: (objectData) => (objectData.system ? 'text' : 'object'),
|
||||||
objectType: (objectData) => {
|
objectType: (objectData) => {
|
||||||
return objectData.ownerType
|
return objectData.ownerType
|
||||||
},
|
},
|
||||||
columnFixed: 'left',
|
columnFixed: 'left',
|
||||||
value: null,
|
value: (objectData) => (objectData.system ? 'System' : objectData.owner),
|
||||||
showCopy: true,
|
showCopy: true,
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'system',
|
||||||
|
label: 'System',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 100
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
label: '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' },
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
key: 'hosts',
|
key: 'hosts',
|
||||||
|
|||||||
@ -69,6 +69,19 @@ const DocumentTemplateDesign = lazy(
|
|||||||
() =>
|
() =>
|
||||||
import('../components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx')
|
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 Files = lazy(() => import('../components/Dashboard/Management/Files.jsx'))
|
||||||
const TaxRates = lazy(
|
const TaxRates = lazy(
|
||||||
() => import('../components/Dashboard/Management/TaxRates.jsx')
|
() => import('../components/Dashboard/Management/TaxRates.jsx')
|
||||||
@ -146,6 +159,26 @@ const ManagementRoutes = [
|
|||||||
path='management/documenttemplates/design'
|
path='management/documenttemplates/design'
|
||||||
element={<DocumentTemplateDesign />}
|
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
|
<Route
|
||||||
key='apppasswords'
|
key='apppasswords'
|
||||||
path='management/apppasswords'
|
path='management/apppasswords'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user