Compare commits

...

2 Commits

Author SHA1 Message Date
eea7e1f724 Add InfoActionButtons to Various Dashboard Components
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Integrated InfoActionButtons into multiple components across the Finance and Inventory sections, enhancing user interaction and providing contextual actions for invoices, payment policies, payments, tax records, and various inventory items.
- Updated the respective components to include the new buttons with appropriate object types and list mode settings, improving the overall functionality and user experience in the dashboard.
2026-09-14 23:33:03 +01:00
8adf2d7847 Refactor HostDeviceScanner for Enhanced Scanning Logic and State Management
- Introduced refs to manage scanning state and active host ID, improving control over scan initiation and termination.
- Updated useEffect to handle scanning conditions more effectively, including debouncing stop scan actions.
- Enhanced session management and error handling during network scans, ensuring better user feedback and reliability.
- Cleaned up component lifecycle management to prevent memory leaks and ensure proper resource cleanup.
2026-09-14 23:11:21 +01:00
68 changed files with 487 additions and 94 deletions

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const Invoices = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='invoice' />
<InfoActionButtons type='invoice' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const PaymentPolicies = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='paymentPolicy' />
<InfoActionButtons type='paymentPolicy' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const Payments = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='payment' />
<InfoActionButtons type='payment' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const TaxRecords = () => {
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='taxRecord' />
<InfoActionButtons type='taxRecord' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -15,6 +15,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -56,6 +57,7 @@ const FilamentStocks = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filamentStock' />
<InfoActionButtons type='filamentStock' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const OrderItems = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='orderItem' />
<InfoActionButtons type='orderItem' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -15,6 +15,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -56,6 +57,7 @@ const PartStocks = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='partStock' />
<InfoActionButtons type='partStock' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -16,6 +16,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -57,6 +58,7 @@ const ProductStocks = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='productStock' />
<InfoActionButtons type='productStock' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const PurchaseOrders = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='purchaseOrder' />
<InfoActionButtons type='purchaseOrder' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const Shipments = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='shipment' />
<InfoActionButtons type='shipment' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -15,6 +15,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -56,6 +57,7 @@ const StockAudits = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockAudit' />
<InfoActionButtons type='stockAudit' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const StockEvents = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockEvent' />
<InfoActionButtons type='stockEvent' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const StockLocations = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockLocation' />
<InfoActionButtons type='stockLocation' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const StockTransfers = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockTransfer' />
<InfoActionButtons type='stockTransfer' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import SortSidebarButton from '../common/SortSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const AppPasswords = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='appPassword' />
<InfoActionButtons type='appPassword' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
@ -49,6 +50,7 @@ const AuditLogs = () => {
updateVisibleState={updateColumnVisibility}
/>
<ExportListButton objectType='auditLog' />
<InfoActionButtons type='auditLog' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const CourierServices = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='courierService' />
<InfoActionButtons type='courierService' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const Couriers = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='courier' />
<InfoActionButtons type='courier' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const DocumentJobs = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='documentJob' />
<InfoActionButtons type='documentJob' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -8,6 +8,7 @@ import { ApiServerContext } from '../../context/ApiServerContext'
import { useCallback, useContext, useEffect, useRef, useState } from 'react'
import { Modal } from 'antd'
import dayjs from 'dayjs'
import { toTemplateObjectData } from '../../utils/templateObjectData'
const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
const {
@ -125,7 +126,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
}}
>
<TemplatePreview
objectData={objectData?.object}
objectData={toTemplateObjectData(objectData?.objects)}
documentTemplate={objectData?.documentTemplate}
/>
</div>
@ -158,7 +159,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
toTemplateObjectData(objectData.objects),
fileName,
'pdf',
() => {
@ -175,7 +176,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
toTemplateObjectData(objectData.objects),
fileName,
'png',
() => {
@ -192,7 +193,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
toTemplateObjectData(objectData.objects),
fileName,
'jpeg',
() => {
@ -209,7 +210,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
toTemplateObjectData(objectData.objects),
fileName,
'svg',
() => {

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const DocumentPrinters = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='documentPrinter' />
<InfoActionButtons type='documentPrinter' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const DocumentSizes = () => {
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='documentSize' />
<InfoActionButtons type='documentSize' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const DocumentTemplates = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='documentTemplate' />
<InfoActionButtons type='documentTemplate' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const EmailAccounts = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='emailAccount' />
<InfoActionButtons type='emailAccount' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const EmailMessages = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='emailMessage' />
<InfoActionButtons type='emailMessage' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -14,7 +14,8 @@ 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'
import { resolveEmailRecipientsForObjects } from '../../utils/emailRecipients'
import { toTemplateObjectData } from '../../utils/templateObjectData'
const progressByState = {
queued: 10,
@ -46,9 +47,13 @@ const EmailWizardContent = ({
return
}
let cancelled = false
resolveEmailRecipients({
resolveEmailRecipientsForObjects({
type: objectData?.objectType,
objectData: objectData?.object,
objects: Array.isArray(objectData?.objects)
? objectData.objects
: objectData?.object
? [objectData.object]
: [],
fetchObject
}).then((result) => {
if (!cancelled) setCandidates(result)
@ -60,6 +65,7 @@ const EmailWizardContent = ({
defaultCandidates,
fetchObject,
objectData?.object,
objectData?.objects,
objectData?.objectType
])
@ -110,7 +116,7 @@ const EmailWizardContent = ({
visibleProperties={{
name: true,
objectType: true,
object: true,
objects: true,
emailTemplate: true,
emailAccount: true,
recipientEmail: true,
@ -142,7 +148,7 @@ const EmailWizardContent = ({
}}
>
<TemplatePreview
objectData={objectData?.object}
objectData={toTemplateObjectData(objectData?.objects)}
template={objectData?.emailTemplate}
templateType='emailTemplate'
capabilities={{

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const EmailTemplates = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='emailTemplate' />
<InfoActionButtons type='emailTemplate' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const FilamentSkus = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filamentSku' />
<InfoActionButtons type='filamentSku' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -58,6 +59,7 @@ const Filaments = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filament' />
<InfoActionButtons type='filament' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -50,6 +51,7 @@ const Files = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='file' />
<InfoActionButtons type='file' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -7,6 +7,7 @@ import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -57,6 +58,7 @@ const Hosts = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='host' />
<InfoActionButtons type='host' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import SortSidebarButton from '../common/SortSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const Materials = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='material' />
<InfoActionButtons type='material' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const NoteTypes = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='noteType' />
<InfoActionButtons type='noteType' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -53,6 +54,7 @@ const PartSkus = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='partSku' />
<InfoActionButtons type='partSku' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -18,6 +18,7 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -57,6 +58,7 @@ const Parts = (filter) => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='part' />
<InfoActionButtons type='part' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const PermissionSettingsList = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='permissionSetting' />
<InfoActionButtons type='permissionSetting' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import SortSidebarButton from '../common/SortSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const ProductCategories = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='productCategory' />
<InfoActionButtons type='productCategory' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -54,6 +55,7 @@ const ProductSkus = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='productSku' />
<InfoActionButtons type='productSku' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -29,6 +29,7 @@ import SortSidebarButton from '../common/SortSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -320,6 +321,7 @@ const Products = () => {
onReload={() => tableRef.current?.reload()}
/>
<ExportListButton objectType='product' />
<InfoActionButtons type='product' listMode={true} />
<Popover
content={getViewDropdownItems()}
placement='bottomLeft'

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const StockAuditLevels = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockAuditLevel' />
<InfoActionButtons type='stockAuditLevel' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const TaxRates = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='taxRate' />
<InfoActionButtons type='taxRate' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const UserGroups = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='userGroup' />
<InfoActionButtons type='userGroup' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const Users = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='user' />
<InfoActionButtons type='user' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -50,6 +51,7 @@ const Vendors = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='vendor' />
<InfoActionButtons type='vendor' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -2,6 +2,7 @@ import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -49,6 +50,7 @@ const FilamentProfiles = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filamentProfile' />
<InfoActionButtons type='filamentProfile' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -14,6 +14,7 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -55,6 +56,7 @@ const GCodeFiles = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='gcodeFile' />
<InfoActionButtons type='gcodeFile' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton.jsx'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -53,6 +54,7 @@ const Jobs = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='job' />
<InfoActionButtons type='job' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -2,6 +2,7 @@ import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const PrinterProfiles = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='printerProfile' />
<InfoActionButtons type='printerProfile' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -7,6 +7,7 @@ import ObjectActions from '../common/ObjectActions'
import BulkObjectActions from '../common/BulkObjectActions'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -58,6 +59,7 @@ const Printers = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='printer' />
<InfoActionButtons type='printer' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -13,6 +13,7 @@ import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton.jsx'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -53,6 +54,7 @@ const SubJobs = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='subJob' />
<InfoActionButtons type='subJob' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -51,6 +52,7 @@ const Clients = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='client' />
<InfoActionButtons type='client' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -48,6 +49,7 @@ const FulfillmentPolicies = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='fulfillmentPolicy' />
<InfoActionButtons type='fulfillmentPolicy' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const ListingVarients = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='listingVarient' />
<InfoActionButtons type='listingVarient' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const Listings = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='listing' />
<InfoActionButtons type='listing' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const Marketplaces = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='marketplace' />
<InfoActionButtons type='marketplace' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -47,6 +48,7 @@ const ReturnPolicies = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='returnPolicy' />
<InfoActionButtons type='returnPolicy' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -11,6 +11,7 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import InfoActionButtons from '../common/InfoActionButtons'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
@ -52,6 +53,7 @@ const SalesOrders = () => {
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='salesOrder' />
<InfoActionButtons type='salesOrder' listMode={true} />
</Space>
<ListViewTabs />
<Space>

View File

@ -7,33 +7,39 @@ import {
useMemo,
useRef
} from 'react'
// import { getModelByName } from '../../../database/ObjectModels'
import DocumentPrinterIcon from '../../Icons/DocumentPrinterIcon'
import { Button, Dropdown, Modal } from 'antd'
import { Button, Dropdown, Modal, message } from 'antd'
import { ApiServerContext } from '../context/ApiServerContext'
import { TableStateContext } from '../context/TableStateContext'
import DocumentTemplateIcon from '../../Icons/DocumentTemplateIcon'
import NewDocumentJob from '../Management/DocumentJobs/NewDocumentJob'
import { AuthContext } from '../context/AuthContext'
import LoadingPleaseWaitModal from './LoadingPleaseWaitModal'
import { fetchAllObjectPages } from '../utils/fetchAllObjectPages'
import { resolveObjectsList } from '../utils/templateObjectData'
const DocumentPrintButton = ({
type,
objectData,
objects,
listMode = false,
disabled = false,
...buttonProps
}) => {
const {
fetchObjects,
connected,
subscribeToObjectUpdates,
subscribeToObjectTypeUpdates
} = useContext(ApiServerContext)
const { fetchObjects, connected, subscribeToObjectUpdates, subscribeToObjectTypeUpdates } =
useContext(ApiServerContext)
const tableState = useContext(TableStateContext)
const fetchObjectsRef = useRef(fetchObjects)
fetchObjectsRef.current = fetchObjects
const [documentTemplates, setDocumentTemplates] = useState([])
const [currentDocumentTemplate, setCurrentDocumentTemplate] = useState(null)
const [loading, setLoading] = useState(false)
const [listLoading, setListLoading] = useState(false)
const [newDocumentJobOpen, setNewDocumentJobOpen] = useState(false)
const [selectedObjects, setSelectedObjects] = useState(() =>
resolveObjectsList(objects, objectData)
)
const subscribedIdsRef = useRef([])
const unsubscribesRef = useRef([])
@ -42,6 +48,11 @@ const DocumentPrintButton = ({
const { token } = useContext(AuthContext)
const resolvedObjects = useMemo(
() => resolveObjectsList(objects, objectData),
[objects, objectData]
)
const subscriptionFilter = useMemo(
() => ({
objectType: type,
@ -69,7 +80,7 @@ const DocumentPrintButton = ({
try {
const result = await fetchObjectsRef.current('documentTemplate', {
filter: subscriptionFilter,
limit: 100 // Get more templates to show in dropdown
limit: 100
})
if (result && result.data) {
@ -89,13 +100,13 @@ const DocumentPrintButton = ({
const loadDocumentTemplatesRef = useRef(loadDocumentTemplates)
loadDocumentTemplatesRef.current = loadDocumentTemplates
// Stable key from objectData._id (excludes _isEditing so toggling edit mode doesn't trigger template reload)
const objectKey = useMemo(() => {
if (!objectData) return null
return objectData._id ?? objectData.id ?? null
}, [objectData])
if (listMode) return 'list'
const first = resolvedObjects[0]
if (!first) return null
return first._id ?? first.id ?? null
}, [listMode, resolvedObjects])
// Fetch document templates when component mounts or objectData, type or token changes
useEffect(() => {
loadDocumentTemplates()
}, [objectKey, token, type, loadDocumentTemplates])
@ -147,7 +158,9 @@ const DocumentPrintButton = ({
useEffect(() => {
if (connected !== true) return
const templateIds = documentTemplates.map((template) => template._id).filter(Boolean)
const templateIds = documentTemplates
.map((template) => template._id)
.filter(Boolean)
const newTemplateIds = templateIds.filter(
(id) => !subscribedIdsRef.current.includes(id)
@ -197,15 +210,40 @@ const DocumentPrintButton = ({
}
}, [])
// Handle template selection
const handleTemplateSelect = (template) => {
setCurrentDocumentTemplate(template)
setNewDocumentJobOpen(true)
// TODO: Implement the actual printing logic here
// This could open a print dialog, navigate to a print page, etc.
}
const handleTemplateSelect = useCallback(
async (template) => {
setCurrentDocumentTemplate(template)
if (listMode) {
setListLoading(true)
try {
const list = await fetchAllObjectPages({
fetchObjects: fetchObjectsRef.current,
type,
filter: tableState?.getObjectListFilter?.(type) || {},
sorter: tableState?.getObjectListSorter?.(type) || {}
})
if (!list.length) {
message.warning('No objects in this list to print')
return
}
setSelectedObjects(list)
setNewDocumentJobOpen(true)
} catch (error) {
console.error('Error fetching objects for document job:', error)
message.error('Failed to load objects for printing')
} finally {
setListLoading(false)
}
return
}
setSelectedObjects(resolvedObjects)
setNewDocumentJobOpen(true)
},
[listMode, resolvedObjects, tableState, type]
)
// Group templates by tag for nested dropdown menu; untagged templates at root
const menuItems = useMemo(() => {
const templatesByTag = new Map()
const untaggedTemplates = []
@ -228,9 +266,9 @@ const DocumentPrintButton = ({
}
for (const tag of templateTags) {
const templates = templatesByTag.get(tag) ?? []
templates.push(template)
templatesByTag.set(tag, templates)
const tagged = templatesByTag.get(tag) ?? []
tagged.push(template)
templatesByTag.set(tag, tagged)
}
}
@ -252,9 +290,14 @@ const DocumentPrintButton = ({
.map(toTemplateMenuItem)
return [...rootUntaggedItems, ...taggedMenuItems]
}, [documentTemplates])
}, [documentTemplates, handleTemplateSelect])
const unavailable =
disabled ||
loading ||
listLoading ||
(!listMode && resolvedObjects.length === 0)
// If no templates available, show disabled state
if (documentTemplates.length === 0 && !loading) {
return (
<Button
@ -274,16 +317,17 @@ const DocumentPrintButton = ({
loading: loading
}}
trigger={['hover']}
disabled={disabled || loading}
disabled={unavailable}
>
<Button
{...buttonProps}
icon={<DocumentPrinterIcon />}
disabled={disabled || loading}
loading={loading}
disabled={unavailable}
loading={loading || listLoading}
title={loading ? 'Loading templates...' : 'Print document'}
/>
</Dropdown>
<LoadingPleaseWaitModal open={listLoading} />
<Modal
open={newDocumentJobOpen}
onCancel={() => setNewDocumentJobOpen(false)}
@ -305,7 +349,7 @@ const DocumentPrintButton = ({
reset={!newDocumentJobOpen}
defaultValues={{
objectType: type,
object: objectData,
objects: selectedObjects,
documentTemplate: currentDocumentTemplate
}}
/>
@ -317,7 +361,9 @@ const DocumentPrintButton = ({
DocumentPrintButton.propTypes = {
type: PropTypes.string.isRequired,
disabled: PropTypes.bool,
objectData: PropTypes.object.isRequired
objectData: PropTypes.object,
objects: PropTypes.array,
listMode: PropTypes.bool
}
export default DocumentPrintButton

View File

@ -7,18 +7,30 @@ import {
useRef,
useState
} from 'react'
import { Button, Dropdown, Modal } from 'antd'
import { Button, Dropdown, Modal, message } from 'antd'
import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import { TableStateContext } from '../context/TableStateContext'
import MailIcon from '../../Icons/MailIcon'
import EmailTemplateIcon from '../../Icons/EmailTemplateIcon'
import NewEmailMessage from '../Management/EmailMessages/NewEmailMessage'
import {
getDefaultEmailRecipient,
resolveEmailRecipients
resolveEmailRecipients,
resolveEmailRecipientsForObjects
} from '../utils/emailRecipients'
import LoadingPleaseWaitModal from './LoadingPleaseWaitModal'
import { fetchAllObjectPages } from '../utils/fetchAllObjectPages'
import { resolveObjectsList } from '../utils/templateObjectData'
const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps }) => {
const EmailSendButton = ({
type,
objectData,
objects,
listMode = false,
disabled = false,
...buttonProps
}) => {
const {
fetchObject,
fetchObjects,
@ -27,16 +39,26 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
subscribeToObjectTypeUpdates
} = useContext(ApiServerContext)
const { token } = useContext(AuthContext)
const tableState = useContext(TableStateContext)
const [templates, setTemplates] = useState([])
const [recipients, setRecipients] = useState([])
const [selectedTemplate, setSelectedTemplate] = useState(null)
const [selectedObjects, setSelectedObjects] = useState(() =>
resolveObjectsList(objects, objectData)
)
const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [listLoading, setListLoading] = useState(false)
const fetchObjectsRef = useRef(fetchObjects)
const fetchObjectRef = useRef(fetchObject)
fetchObjectsRef.current = fetchObjects
fetchObjectRef.current = fetchObject
const resolvedObjects = useMemo(
() => resolveObjectsList(objects, objectData),
[objects, objectData]
)
const filter = useMemo(
() => ({ objectType: type, global: false, active: true }),
[type]
@ -71,11 +93,15 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
}, [loadTemplates])
useEffect(() => {
if (listMode) {
setRecipients([])
return undefined
}
let cancelled = false
setRecipients([])
resolveEmailRecipients({
type,
objectData,
objectData: resolvedObjects[0],
fetchObject: (...args) => fetchObjectRef.current(...args)
}).then((result) => {
if (!cancelled) setRecipients(result)
@ -83,19 +109,14 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
return () => {
cancelled = true
}
}, [objectData, type])
}, [listMode, resolvedObjects, type])
useEffect(() => {
if (!connected) return undefined
return subscribeToObjectTypeUpdates('emailTemplate', filter, () =>
loadTemplates(true)
)
}, [
connected,
filter,
loadTemplates,
subscribeToObjectTypeUpdates
])
}, [connected, filter, loadTemplates, subscribeToObjectTypeUpdates])
useEffect(() => {
if (!connected) return undefined
@ -121,6 +142,50 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
return () => unsubscribes.forEach((unsubscribe) => unsubscribe?.())
}, [connected, matches, subscribeToObjectUpdates, templates])
const handleTemplateSelect = useCallback(
async (template) => {
setSelectedTemplate(template)
if (listMode) {
setListLoading(true)
try {
const list = await fetchAllObjectPages({
fetchObjects: fetchObjectsRef.current,
type,
filter: tableState?.getObjectListFilter?.(type) || {},
sorter: tableState?.getObjectListSorter?.(type) || {}
})
if (!list.length) {
message.warning('No objects in this list to email')
return
}
const nextRecipients = await resolveEmailRecipientsForObjects({
type,
objects: list,
fetchObject: (...args) => fetchObjectRef.current(...args)
})
if (!nextRecipients.length) {
message.warning('No email recipients available for these objects')
return
}
setSelectedObjects(list)
setRecipients(nextRecipients)
setOpen(true)
} catch (error) {
console.error('Error fetching objects for email:', error)
message.error('Failed to load objects for email')
} finally {
setListLoading(false)
}
return
}
setSelectedObjects(resolvedObjects)
setOpen(true)
},
[listMode, resolvedObjects, tableState, type]
)
const menuItems = useMemo(() => {
const byTag = new Map()
const untagged = []
@ -129,8 +194,7 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
label: template.name,
icon: <EmailTemplateIcon />,
onClick: () => {
setSelectedTemplate(template)
setOpen(true)
handleTemplateSelect(template)
}
})
@ -159,19 +223,28 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
.map(makeItem)
}))
]
}, [templates])
}, [handleTemplateSelect, 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
const unavailable =
disabled ||
loading ||
listLoading ||
!templates.length ||
(!listMode && (!resolvedObjects.length || !recipients.length))
const title = listMode
? !templates.length
? 'No email templates available for this object type'
: 'Send email'
: !recipients.length
? 'No email recipients available for this object'
: !templates.length
? 'No email templates available for this object type'
: 'Send email'
return (
<>
@ -180,10 +253,11 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
{...buttonProps}
icon={<MailIcon />}
disabled={unavailable}
loading={loading}
loading={loading || listLoading}
title={title}
/>
</Dropdown>
<LoadingPleaseWaitModal open={listLoading} />
<Modal
open={open}
onCancel={() => setOpen(false)}
@ -195,7 +269,7 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
onOk={() => setOpen(false)}
defaultValues={{
objectType: type,
object: objectData,
objects: selectedObjects,
emailTemplate: selectedTemplate,
...defaultRecipient
}}
@ -208,7 +282,9 @@ const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps })
EmailSendButton.propTypes = {
type: PropTypes.string.isRequired,
objectData: PropTypes.object.isRequired,
objectData: PropTypes.object,
objects: PropTypes.array,
listMode: PropTypes.bool,
disabled: PropTypes.bool
}

View File

@ -52,6 +52,9 @@ const HostDeviceScanner = ({
? crypto.randomUUID()
: `scan-${Date.now()}-${Math.random().toString(16).slice(2)}`
)
const startedRef = useRef(false)
const activeHostIdRef = useRef(null)
const pendingStopRef = useRef(null)
const [scanning, setScanning] = useState(false)
const [progress, setProgress] = useState(0)
@ -80,22 +83,52 @@ const HostDeviceScanner = ({
}
useEffect(() => {
if (!hostId || connected !== true || !compatible) {
return
const sessionId = sessionIdRef.current
const canScan = Boolean(hostId) && connected === true && compatible
const stopScan = (targetHostId) => {
if (!targetHostId || !startedRef.current) {
return
}
sendObjectActionRef.current(targetHostId, 'host', {
type: 'scanNetworkStop',
data: { sessionId }
})
startedRef.current = false
activeHostIdRef.current = null
}
if (pendingStopRef.current) {
clearTimeout(pendingStopRef.current)
pendingStopRef.current = null
}
if (!canScan) {
const hostToStop = activeHostIdRef.current
pendingStopRef.current = setTimeout(() => {
stopScan(hostToStop)
pendingStopRef.current = null
}, 250)
return () => {
if (pendingStopRef.current) {
clearTimeout(pendingStopRef.current)
pendingStopRef.current = null
}
}
}
let cancelled = false
const sessionId = sessionIdRef.current
console.log('Scan session id:', sessionId)
const isSessionEvent = (event) => event?.data?.sessionId === sessionId
const startScan = () => {
if (cancelled) return
setProgress(0)
setCurrentIP(null)
setScanError(null)
setScanning(true)
setDevices([])
setSelectedKey(null)
sendObjectActionRef.current(
hostId,
@ -113,6 +146,7 @@ const HostDeviceScanner = ({
if (result?.success === false) {
setScanning(false)
setScanError(result.error || 'Failed to start network scan')
startedRef.current = false
}
}
)
@ -192,29 +226,27 @@ const HostDeviceScanner = ({
onComplete
)
setDevices([])
setSelectedKey(null)
startScan()
const alreadyRunning = startedRef.current && activeHostIdRef.current === hostId
if (!alreadyRunning) {
if (startedRef.current && activeHostIdRef.current) {
stopScan(activeHostIdRef.current)
}
startedRef.current = true
activeHostIdRef.current = hostId
startScan()
}
return () => {
cancelled = true
if (unsubFound) unsubFound()
if (unsubProgress) unsubProgress()
if (unsubComplete) unsubComplete()
sendObjectActionRef.current(hostId, 'host', {
type: 'scanNetworkStop',
data: { sessionId }
})
pendingStopRef.current = setTimeout(() => {
stopScan(hostId)
pendingStopRef.current = null
}, 250)
}
}, [
hostId,
connected,
compatible,
port,
protocol,
connectionInterface,
objectType
])
}, [hostId, connected, compatible])
const applyDevice = useCallback(
(device) => {

View File

@ -25,7 +25,9 @@ const InfoActionButtons = (props) => {
InfoActionButtons.propTypes = {
type: PropTypes.string.isRequired,
disabled: PropTypes.bool,
objectData: PropTypes.object.isRequired
objectData: PropTypes.object,
objects: PropTypes.array,
listMode: PropTypes.bool
}
export default InfoActionButtons

View File

@ -0,0 +1,30 @@
import PropTypes from 'prop-types'
import { Modal, Space, Typography } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
const { Text } = Typography
const LoadingPleaseWaitModal = ({ open = false }) => (
<Modal
open={open}
className='loading-modal'
title={false}
height={20}
style={{ maxWidth: 220, top: '50%', transform: 'translateY(-50%)' }}
closable={false}
maskClosable={false}
footer={false}
zIndex={2000}
>
<Space size={'middle'}>
<LoadingOutlined />
<Text style={{ margin: 0 }}>Loading, please wait...</Text>
</Space>
</Modal>
)
LoadingPleaseWaitModal.propTypes = {
open: PropTypes.bool
}
export default LoadingPleaseWaitModal

View File

@ -10,7 +10,10 @@ import {
import { useSearchParams } from 'react-router-dom'
import PropTypes from 'prop-types'
const TableStateContext = createContext()
const TableStateContext = createContext(null)
// eslint-disable-next-line react-refresh/only-export-components
export { TableStateContext }
export const FILTER_URL_PARAM = 'filter'
export const SORT_URL_PARAM = 'sort'

View File

@ -79,6 +79,26 @@ export const resolveEmailRecipients = async ({
return candidates
}
export const resolveEmailRecipientsForObjects = async ({
type,
objects,
fetchObject
}) => {
const list = Array.isArray(objects) ? objects.filter(Boolean) : []
const merged = []
for (const objectData of list) {
const result = await resolveEmailRecipients({
type,
objectData,
fetchObject
})
for (const candidate of result) {
addCandidate(merged, candidate)
}
}
return merged
}
/** Prefer a client/vendor ref; otherwise use a plain email candidate. */
export const getDefaultEmailRecipient = (candidates = []) => {
if (!Array.isArray(candidates) || !candidates.length) return null

View File

@ -0,0 +1,41 @@
export async function fetchAllObjectPages({
fetchObjects,
type,
filter = {},
sorter = {},
limit = 25
}) {
if (!type || typeof fetchObjects !== 'function') return []
const all = []
let page = 1
let totalPages = 1
do {
const result = await fetchObjects(type, {
page,
limit,
filter,
sorter
})
if (!result || Array.isArray(result)) {
break
}
const data = Array.isArray(result.data) ? result.data : []
all.push(...data)
if (Number.isFinite(result.totalPages) && result.totalPages > 0) {
totalPages = result.totalPages
} else if (result.hasMore) {
totalPages = page + 1
} else {
totalPages = page
}
page += 1
} while (page <= totalPages)
return all
}

View File

@ -0,0 +1,21 @@
export function resolveObjectsList(objects, objectData) {
if (Array.isArray(objects) && objects.length) {
return objects.filter(Boolean)
}
if (objectData) {
return [objectData]
}
return []
}
export function toTemplateObjectData(objects) {
const list = Array.isArray(objects) ? objects.filter(Boolean) : []
if (!list.length) return {}
if (list.length === 1) {
const first = list[0]
if (first && typeof first === 'object' && !Array.isArray(first)) {
return first
}
}
return { objects: list }
}

View File

@ -166,7 +166,12 @@ export const DocumentJob = {
columnFixed: 'left',
value: (objectData) => {
if (objectData?.createdAt == undefined) {
return `${objectData?.documentTemplate?.name || 'No template'} ${dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')} (${objectData?.object?.name || objectData?.object?._id})`
const list = Array.isArray(objectData?.objects)
? objectData.objects
: []
const first = list[0]
const extra = list.length > 1 ? ` +${list.length - 1}` : ''
return `${objectData?.documentTemplate?.name || 'No template'} ${dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')} (${first?.name || first?._id || 'no object'}${extra})`
}
}
},
@ -188,11 +193,11 @@ export const DocumentJob = {
type: 'objectType'
},
{
name: 'object',
label: 'Object',
name: 'objects',
label: 'Objects',
required: true,
columnWidth: 150,
type: 'object',
type: 'objectList',
objectType: (objectData) => {
return objectData?.objectType
},

View File

@ -220,9 +220,9 @@ export const EmailMessage = {
columnWidth: 180
},
{
name: 'object',
label: 'Object',
type: 'object',
name: 'objects',
label: 'Objects',
type: 'objectList',
objectType: (data) => data?.objectType,
required: true,
readOnly: readOnlyAfterCreate,