Refactor modal behavior across multiple components to use 'destroyOnHidden' instead of 'destroyOnClose' for improved resource management. This change applies to AppPasswordInfo, FilamentInfo, PartInfo, ProductInfo, UserInfo, MarketplaceInfo, and ExportListButton components.

This commit is contained in:
Tom Butcher 2026-07-23 21:41:46 +01:00
parent 899444e815
commit 634be89698
11 changed files with 140 additions and 49 deletions

View File

@ -191,7 +191,7 @@ const AppPasswordInfo = () => {
<Modal <Modal
open={regenerateSecretOpen} open={regenerateSecretOpen}
destroyOnClose destroyOnHidden={true}
width={650} width={650}
onCancel={() => { onCancel={() => {
actionHandlerRef.current?.clearAction?.() actionHandlerRef.current?.clearAction?.()

View File

@ -229,7 +229,7 @@ const FilamentInfo = () => {
footer={null} footer={null}
width={700} width={700}
onCancel={() => setNewFilamentSkuOpen(false)} onCancel={() => setNewFilamentSkuOpen(false)}
destroyOnClose destroyOnHidden={true}
> >
<NewFilamentSku <NewFilamentSku
onOk={() => { onOk={() => {

View File

@ -184,7 +184,7 @@ const PartInfo = () => {
footer={null} footer={null}
width={700} width={700}
onCancel={() => setNewPartSkuOpen(false)} onCancel={() => setNewPartSkuOpen(false)}
destroyOnClose destroyOnHidden={true}
> >
<NewPartSku <NewPartSku
onOk={() => { onOk={() => {

View File

@ -188,7 +188,7 @@ const ProductInfo = () => {
footer={null} footer={null}
width={700} width={700}
onCancel={() => setNewProductSkuOpen(false)} onCancel={() => setNewProductSkuOpen(false)}
destroyOnClose destroyOnHidden={true}
> >
<NewProductSku <NewProductSku
onOk={() => { onOk={() => {

View File

@ -239,7 +239,7 @@ const UserInfo = () => {
<Modal <Modal
open={newAppPasswordOpen} open={newAppPasswordOpen}
destroyOnClose destroyOnHidden={true}
width={700} width={700}
onCancel={() => { onCancel={() => {
actionHandlerRef.current?.clearAction?.() actionHandlerRef.current?.clearAction?.()

View File

@ -1,6 +1,9 @@
import { useRef, useState } from 'react' import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd' import { Space, Flex, Card, Button, Modal } from 'antd'
import NewPrinterProfile, {
EditPrinterProfile
} from '../PrinterProfiles/NewPrinterProfile'
import { LoadingOutlined } from '@ant-design/icons' import { LoadingOutlined } from '@ant-design/icons'
import loglevel from 'loglevel' import loglevel from 'loglevel'
import config from '../../../../config.js' import config from '../../../../config.js'
@ -22,17 +25,20 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx' import DocumentPrintButton from '../../common/DocumentPrintButton.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 PlusIcon from '../../../Icons/PlusIcon.jsx'
import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
const log = loglevel.getLogger('PrinterInfo') const log = loglevel.getLogger('PrinterInfo')
log.setLevel(config.logLevel) log.setLevel(config.logLevel)
const PrinterInfo = () => { const PrinterInfo = () => {
const location = useLocation() const location = useLocation()
const objectFormRef = useRef(null) const objectFormRef = useRef(null)
const profilesTableRef = useRef(null)
const actionHandlerRef = useRef(null) const actionHandlerRef = useRef(null)
const printerId = new URLSearchParams(location.search).get('printerId') const printerId = new URLSearchParams(location.search).get('printerId')
const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', { const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', {
info: true, info: true,
profiles: true,
stocks: true, stocks: true,
notes: true, notes: true,
auditLogs: false auditLogs: false
@ -46,6 +52,15 @@ const PrinterInfo = () => {
loading: false, loading: false,
objectData: {} objectData: {}
}) })
const [profileModal, setProfileModal] = useState({
open: false,
profileId: null
})
const handleProfileSaved = () => {
setProfileModal({ open: false, profileId: null })
profilesTableRef.current?.reload()
}
const actions = { const actions = {
edit: () => { edit: () => {
@ -85,6 +100,7 @@ const PrinterInfo = () => {
disabled={objectFormState.loading} disabled={objectFormState.loading}
items={[ items={[
{ key: 'info', label: 'Printer Information' }, { key: 'info', label: 'Printer Information' },
{ key: 'profiles', label: 'Printer Profiles' },
{ key: 'notes', label: 'Notes' }, { key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' } { key: 'auditLogs', label: 'Audit Logs' }
]} ]}
@ -147,31 +163,73 @@ const PrinterInfo = () => {
setEditFormState((prev) => ({ ...prev, ...state })) setEditFormState((prev) => ({ ...prev, ...state }))
}} }}
> >
{({ loading, isEditing, objectData }) => { {({ loading, isEditing, objectData }) => (
return ( <ObjectInfo
<ObjectInfo loading={loading}
loading={loading} indicator={<LoadingOutlined />}
indicator={<LoadingOutlined />} isEditing={isEditing}
isEditing={isEditing} type='printer'
type='printer' objectData={objectData}
objectData={objectData} labelWidth='175px'
labelWidth='175px' visibleProperties={{
visibleProperties={{ currentFilamentStock: false,
currentFilamentStock: false, 'currentFilamentStock._id': false,
'currentFilamentStock._id': false, currentJob: false,
currentJob: false, 'currentJob._id': false,
'currentJob._id': false, currentSubJob: false,
currentSubJob: false, 'currentSubJob._id': false,
'currentSubJob._id': false, alerts: false
alerts: false }}
}} />
/> )}
)
}}
</ObjectForm> </ObjectForm>
</InfoCollapse> </InfoCollapse>
</ActionHandler> </ActionHandler>
<InfoCollapse
title='Printer Profiles'
icon={<PrinterProfileIcon />}
active={collapseState.profiles}
onToggle={(expanded) => updateCollapseState('profiles', expanded)}
collapseKey='profiles'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<Flex vertical gap='middle'>
<Flex>
<Button
icon={<PlusIcon />}
onClick={() =>
setProfileModal({ open: true, profileId: null })
}
>
New Printer Profile
</Button>
</Flex>
<ObjectTable
ref={profilesTableRef}
type='printerProfile'
masterFilter={{ printer: printerId }}
visibleColumns={{
printer: false,
'printer._id': false
}}
onRowAction={(action, profile) => {
if (action.name === 'edit') {
setProfileModal({
open: true,
profileId: profile._id
})
return true
}
return false
}}
/>
</Flex>
)}
</InfoCollapse>
<InfoCollapse <InfoCollapse
title='Notes' title='Notes'
icon={<NoteIcon />} icon={<NoteIcon />}
@ -206,6 +264,27 @@ const PrinterInfo = () => {
</Flex> </Flex>
</ScrollBox> </ScrollBox>
</Flex> </Flex>
<Modal
open={profileModal.open}
footer={null}
width={1000}
onCancel={() => setProfileModal({ open: false, profileId: null })}
destroyOnHidden
>
{profileModal.open &&
(profileModal.profileId ? (
<EditPrinterProfile
profileId={profileModal.profileId}
printerId={printerId}
onOk={handleProfileSaved}
/>
) : (
<NewPrinterProfile
printerId={printerId}
onOk={handleProfileSaved}
/>
))}
</Modal>
</> </>
) )
} }

View File

@ -285,7 +285,7 @@ const MarketplaceInfo = () => {
onCancel={() => setSyncListingsOpen(false)} onCancel={() => setSyncListingsOpen(false)}
width={500} width={500}
footer={null} footer={null}
destroyOnClose destroyOnHidden={true}
centered centered
> >
<SyncListings <SyncListings
@ -301,7 +301,7 @@ const MarketplaceInfo = () => {
onCancel={() => setSyncOrdersOpen(false)} onCancel={() => setSyncOrdersOpen(false)}
width={500} width={500}
footer={null} footer={null}
destroyOnClose destroyOnHidden={true}
centered centered
> >
<SyncOrders <SyncOrders
@ -317,7 +317,7 @@ const MarketplaceInfo = () => {
onCancel={() => setConfigureModalOpen(false)} onCancel={() => setConfigureModalOpen(false)}
width={650} width={650}
footer={null} footer={null}
destroyOnClose destroyOnHidden={true}
centered centered
> >
<ConfigureMarketplace <ConfigureMarketplace

View File

@ -105,7 +105,7 @@ const ExportListButton = ({
</Dropdown> </Dropdown>
<Modal <Modal
open={odataModalOpen} open={odataModalOpen}
destroyOnClose destroyOnHidden={true}
width={650} width={650}
onCancel={() => setOdataModalOpen(false)} onCancel={() => setOdataModalOpen(false)}
footer={null} footer={null}
@ -114,7 +114,7 @@ const ExportListButton = ({
</Modal> </Modal>
<Modal <Modal
open={rssModalOpen} open={rssModalOpen}
destroyOnClose destroyOnHidden={true}
width={750} width={750}
onCancel={() => setRssModalOpen(false)} onCancel={() => setRssModalOpen(false)}
footer={null} footer={null}

View File

@ -1,4 +1,4 @@
import { useState } from 'react' import { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Typography, Button, Tag, Badge, Flex, Card, Divider } from 'antd' import { Typography, Button, Tag, Badge, Flex, Card, Divider } from 'antd'
import { import {
@ -31,6 +31,10 @@ const Notification = ({
}) => { }) => {
const [deleting, setDeleting] = useState(false) const [deleting, setDeleting] = useState(false)
useEffect(() => {
console.log('notificationData', JSON.stringify(notification))
}, [notification])
const getNotificationIcon = (type) => { const getNotificationIcon = (type) => {
switch (type) { switch (type) {
case 'info': case 'info':
@ -220,7 +224,7 @@ const Notification = ({
{showExtraInfo && ( {showExtraInfo && (
<> <>
<Divider style={{ margin: largeSpacing ? '10px 0' : 0 }} /> <Divider style={{ margin: largeSpacing ? '10px 0' : 0 }} />
<Flex justify='space-between' align='center'> <Flex ju stify='space-between' align='center'>
{getNotificationTag(notification.type)} {getNotificationTag(notification.type)}
<TimeDisplay <TimeDisplay
dateTime={notification.createdAt} dateTime={notification.createdAt}

View File

@ -47,17 +47,20 @@ const ObjectDisplay = ({
}, []) }, [])
// Detect minimal objects that only contain an _id (must be string, not populated object) // Detect minimal objects that only contain an _id (must be string, not populated object)
const isMinimalObject = useCallback((obj) => { const isMinimalObject = useCallback(
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false (obj) => {
const keys = Object.keys(obj) if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false
const id = obj._id const keys = Object.keys(obj)
return ( const id = obj._id
keys.length === 1 && return (
keys[0] === '_id' && keys.length === 1 &&
typeof id === 'string' && keys[0] === '_id' &&
isValidId(id) typeof id === 'string' &&
) isValidId(id)
}, [isValidId]) )
},
[isValidId]
)
// If only an _id is provided, fetch the full object via spotlight // If only an _id is provided, fetch the full object via spotlight
const fetchFullObjectIfNeeded = useCallback( const fetchFullObjectIfNeeded = useCallback(
@ -131,7 +134,14 @@ const ObjectDisplay = ({
cancelled = true cancelled = true
setIsHydrating(false) setIsHydrating(false)
} }
}, [object, fetchFullObjectIfNeeded, isMinimalObject, isValidId, getStringId, token]) }, [
object,
fetchFullObjectIfNeeded,
isMinimalObject,
isValidId,
getStringId,
token
])
if (!objectData) { if (!objectData) {
return <Text type='secondary'>n/a</Text> return <Text type='secondary'>n/a</Text>
} }

View File

@ -43,8 +43,6 @@ const PropertyChanges = ({ type, value }) => {
...flatNew ...flatNew
} }
console.log(combinedChanges)
return ( return (
<Descriptions size='small' column={1}> <Descriptions size='small' column={1}>
{Object.keys(combinedChanges).map((key) => { {Object.keys(combinedChanges).map((key) => {