Compare commits

..

No commits in common. "80ecc8a17518ec19594321bbf361275dfd4e0f11" and "83f5b20168f9af98a5524347f982bb07d5508707" have entirely different histories.

9 changed files with 365 additions and 266 deletions

View File

@ -1,6 +1,8 @@
import { useContext, useRef, useState } from 'react' import { useContext, useRef, useState } from 'react'
import { Button, Dropdown, Flex, Modal, Space } from 'antd' import { Button, Dropdown, Flex, Modal, Space } from 'antd'
import NewPrinterProfile from './PrinterProfiles/NewPrinterProfile' import NewPrinterProfile, {
EditPrinterProfile
} from './PrinterProfiles/NewPrinterProfile'
import ColumnViewButton from '../common/ColumnViewButton' import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton' import ExportListButton from '../common/ExportListButton'
import FilterSidebarButton from '../common/FilterSidebarButton' import FilterSidebarButton from '../common/FilterSidebarButton'
@ -14,7 +16,10 @@ import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useViewMode from '../hooks/useViewMode' import useViewMode from '../hooks/useViewMode'
const PrinterProfiles = () => { const PrinterProfiles = () => {
const { deleteObject } = useContext(ApiServerContext) const { deleteObject } = useContext(ApiServerContext)
const [newProfileOpen, setNewProfileOpen] = useState(false) const [profileModal, setProfileModal] = useState({
open: false,
profileId: null
})
const tableRef = useRef() const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('PrinterProfiles') const [viewMode, setViewMode] = useViewMode('PrinterProfiles')
const [columnVisibility, setColumnVisibility] = const [columnVisibility, setColumnVisibility] =
@ -40,12 +45,17 @@ const PrinterProfiles = () => {
if (key === 'reloadList') { if (key === 'reloadList') {
tableRef.current?.reload() tableRef.current?.reload()
} else if (key === 'newPrinterProfile') { } else if (key === 'newPrinterProfile') {
setNewProfileOpen(true) setProfileModal({ open: true, profileId: null })
} }
} }
} }
const handleRowAction = (action, profile) => { const handleRowAction = (action, profile) => {
if (action.name === 'edit') {
setProfileModal({ open: true, profileId: profile._id })
return true
}
if (action.name === 'delete') { if (action.name === 'delete') {
Modal.confirm({ Modal.confirm({
title: 'Delete printer profile?', title: 'Delete printer profile?',
@ -102,20 +112,29 @@ const PrinterProfiles = () => {
</Flex> </Flex>
<Modal <Modal
open={newProfileOpen} open={profileModal.open}
footer={null} footer={null}
width={800} width={800}
onCancel={() => setNewProfileOpen(false)} onCancel={() => setProfileModal({ open: false, profileId: null })}
destroyOnHidden destroyOnHidden
> >
{newProfileOpen && ( {profileModal.open &&
<NewPrinterProfile (profileModal.profileId ? (
<EditPrinterProfile
profileId={profileModal.profileId}
onOk={() => { onOk={() => {
setNewProfileOpen(false) setProfileModal({ open: false, profileId: null })
tableRef.current?.reload() tableRef.current?.reload()
}} }}
/> />
)} ) : (
<NewPrinterProfile
onOk={() => {
setProfileModal({ open: false, profileId: null })
tableRef.current?.reload()
}}
/>
))}
</Modal> </Modal>
</> </>
) )

View File

@ -1,6 +1,7 @@
import { useMemo } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import NewObjectForm from '../../common/NewObjectForm' import NewObjectForm from '../../common/NewObjectForm'
import ObjectForm from '../../common/ObjectForm'
import ObjectInfo from '../../common/ObjectInfo' import ObjectInfo from '../../common/ObjectInfo'
import WizardView from '../../common/WizardView' import WizardView from '../../common/WizardView'
@ -143,23 +144,21 @@ const SUMMARY_PROPERTIES = [
...OPTIONAL_PROPERTIES ...OPTIONAL_PROPERTIES
] ]
const NewPrinterProfile = ({ onOk, reset, defaultValues = {} }) => { const PrinterProfileWizard = ({
const mergedDefaultValues = useMemo( objectData,
() => ({ formValid,
...PRUSA_MK3S_PROFILE_DEFAULTS, loading,
...defaultValues onSubmit,
}), editing,
[defaultValues] printerId = null
) }) => {
const visibleProperties = useMemo(() => {
if (!printerId) return undefined
return { printer: false, 'printer._id': false }
}, [printerId])
return ( const steps = useMemo(
<NewObjectForm () => [
type='printerProfile'
reset={reset}
defaultValues={mergedDefaultValues}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{ {
title: 'Required', title: 'Required',
key: 'required', key: 'required',
@ -173,6 +172,7 @@ const NewPrinterProfile = ({ onOk, reset, defaultValues = {} }) => {
labelWidth='75px' labelWidth='75px'
required={true} required={true}
objectData={objectData} objectData={objectData}
visibleProperties={visibleProperties}
/> />
) )
}, },
@ -249,34 +249,124 @@ const NewPrinterProfile = ({ onOk, reset, defaultValues = {} }) => {
isEditing={false} isEditing={false}
labelWidth='210px' labelWidth='210px'
objectData={objectData} objectData={objectData}
visibleProperties={visibleProperties}
/> />
) )
} }
] ],
[objectData, visibleProperties]
)
return ( return (
<WizardView <WizardView
sizeBarWidth='160px' sizeBarWidth='160px'
steps={steps} steps={steps}
loading={submitLoading} loading={loading}
formValid={formValid} formValid={formValid}
title='New Printer Profile' title={editing ? 'Edit Printer Profile' : 'New Printer Profile'}
submitText='Create' submitText={editing ? 'Save' : 'Create'}
onSubmit={onSubmit}
/>
)
}
PrinterProfileWizard.propTypes = {
objectData: PropTypes.object,
formValid: PropTypes.bool.isRequired,
loading: PropTypes.bool,
onSubmit: PropTypes.func.isRequired,
editing: PropTypes.bool,
printerId: PropTypes.string
}
const NewPrinterProfile = ({ onOk, printerId = null }) => (
<NewObjectForm
type='printerProfile'
defaultValues={{
...PRUSA_MK3S_PROFILE_DEFAULTS,
...(printerId ? { printer: printerId } : {})
}}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => (
<PrinterProfileWizard
objectData={objectData}
formValid={formValid}
loading={submitLoading}
printerId={printerId}
onSubmit={async () => { onSubmit={async () => {
const result = await handleSubmit() const result = await handleSubmit()
if (result?._id) onOk(result) if (result?._id) onOk(result)
}} }}
/> />
) )}
}}
</NewObjectForm> </NewObjectForm>
) )
}
NewPrinterProfile.propTypes = { NewPrinterProfile.propTypes = {
onOk: PropTypes.func.isRequired, onOk: PropTypes.func.isRequired,
reset: PropTypes.bool, printerId: PropTypes.string
defaultValues: PropTypes.object }
export const EditPrinterProfile = ({ profileId, onOk, printerId = null }) => {
const formRef = useRef(null)
const startedEditingRef = useRef(false)
const saveRequestedRef = useRef(false)
const stateRef = useRef({})
const [formState, setFormState] = useState({
loading: true,
editLoading: false,
formValid: false,
isEditing: false,
objectData: {}
})
useEffect(() => {
if (
!formState.loading &&
!formState.isEditing &&
!startedEditingRef.current
) {
startedEditingRef.current = true
formRef.current?.startEditing()
}
}, [formState.isEditing, formState.loading])
return (
<ObjectForm
id={profileId}
type='printerProfile'
ref={formRef}
onStateChange={(nextState) => {
stateRef.current = { ...stateRef.current, ...nextState }
setFormState((current) => ({ ...current, ...nextState }))
if (saveRequestedRef.current && nextState.isEditing === false) {
saveRequestedRef.current = false
onOk(stateRef.current.objectData)
}
}}
>
{({ objectData, formValid, editLoading, handleUpdate }) => (
<PrinterProfileWizard
editing
objectData={objectData}
formValid={formValid}
loading={formState.loading || editLoading}
printerId={printerId}
onSubmit={() => {
saveRequestedRef.current = true
handleUpdate()
}}
/>
)}
</ObjectForm>
)
}
EditPrinterProfile.propTypes = {
profileId: PropTypes.string.isRequired,
onOk: PropTypes.func.isRequired,
printerId: PropTypes.string
} }
export default NewPrinterProfile export default NewPrinterProfile

View File

@ -1,7 +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, Modal } from 'antd' import { Space, Flex, Card, Button, Modal } from 'antd'
import NewPrinterProfile from '../PrinterProfiles/NewPrinterProfile' 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'
@ -23,6 +25,7 @@ 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' import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
const log = loglevel.getLogger('PrinterInfo') const log = loglevel.getLogger('PrinterInfo')
log.setLevel(config.logLevel) log.setLevel(config.logLevel)
@ -49,7 +52,15 @@ const PrinterInfo = () => {
loading: false, loading: false,
objectData: {} objectData: {}
}) })
const [newPrinterProfileOpen, setNewPrinterProfileOpen] = useState(false) const [profileModal, setProfileModal] = useState({
open: false,
profileId: null
})
const handleProfileSaved = () => {
setProfileModal({ open: false, profileId: null })
profilesTableRef.current?.reload()
}
const actions = { const actions = {
edit: () => { edit: () => {
@ -63,10 +74,6 @@ const PrinterInfo = () => {
finishEdit: () => { finishEdit: () => {
objectFormRef?.current.handleUpdate() objectFormRef?.current.handleUpdate()
return true return true
},
newPrinterProfile: () => {
setNewPrinterProfileOpen(true)
return true
} }
} }
@ -189,6 +196,17 @@ const PrinterInfo = () => {
{objectFormState.loading ? ( {objectFormState.loading ? (
<InfoCollapsePlaceholder /> <InfoCollapsePlaceholder />
) : ( ) : (
<Flex vertical gap='middle'>
<Flex>
<Button
icon={<PlusIcon />}
onClick={() =>
setProfileModal({ open: true, profileId: null })
}
>
New Printer Profile
</Button>
</Flex>
<ObjectTable <ObjectTable
ref={profilesTableRef} ref={profilesTableRef}
type='printerProfile' type='printerProfile'
@ -197,7 +215,18 @@ const PrinterInfo = () => {
printer: false, printer: false,
'printer._id': false 'printer._id': false
}} }}
onRowAction={(action, profile) => {
if (action.name === 'edit') {
setProfileModal({
open: true,
profileId: profile._id
})
return true
}
return false
}}
/> />
</Flex>
)} )}
</InfoCollapse> </InfoCollapse>
@ -236,25 +265,25 @@ const PrinterInfo = () => {
</ScrollBox> </ScrollBox>
</Flex> </Flex>
<Modal <Modal
open={newPrinterProfileOpen} open={profileModal.open}
styles={{ content: { paddingBottom: '24px' } }}
footer={null} footer={null}
width={800} width={1000}
onCancel={() => { onCancel={() => setProfileModal({ open: false, profileId: null })}
setNewPrinterProfileOpen(false) destroyOnHidden
}}
destroyOnHidden={true}
> >
<NewPrinterProfile {profileModal.open &&
onOk={() => { (profileModal.profileId ? (
setNewPrinterProfileOpen(false) <EditPrinterProfile
profilesTableRef.current?.reload() profileId={profileModal.profileId}
}} printerId={printerId}
reset={newPrinterProfileOpen} onOk={handleProfileSaved}
defaultValues={{
printer: { ...objectFormState.objectData }
}}
/> />
) : (
<NewPrinterProfile
printerId={printerId}
onOk={handleProfileSaved}
/>
))}
</Modal> </Modal>
</> </>
) )

View File

@ -24,8 +24,7 @@ const FileList = ({
showDownload = true, showDownload = true,
defaultPreviewOpen = false, defaultPreviewOpen = false,
minimal = false, minimal = false,
card = true, card = true
maxWidth = '100%'
}) => { }) => {
const { fetchFileContent, flushFile } = useContext(ApiServerContext) const { fetchFileContent, flushFile } = useContext(ApiServerContext)
const navigate = useNavigate() const navigate = useNavigate()
@ -64,23 +63,12 @@ const FileList = ({
const filesToRender = multiple ? files : [files] const filesToRender = multiple ? files : [files]
const renderFileContent = (file) => ( const renderFileContent = (file) => (
<Flex <Flex vertical gap='10px' style={{ maxWidth: '100%', minWidth: 0 }}>
vertical
gap='10px'
style={{ maxWidth: '100%', minWidth: 0, width: card ? '100%' : 'fit-content' }}
>
<Flex <Flex
justify={card ? 'space-between' : 'start'} justify={card ? 'space-between' : 'start'}
align='center' style={{ maxWidth: '100%', minWidth: 0 }}
gap='small'
wrap={false}
style={{ maxWidth: '100%', minWidth: 0, width: card ? '100%' : 'fit-content' }}
>
<Flex
gap={'small'}
align='center'
style={{ minWidth: 0, flex: '0 1 auto', overflow: 'hidden' }}
> >
<Flex gap={'small'} align='center' style={{ minWidth: 0 }}>
<FileIcon <FileIcon
style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }} style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }}
/> />
@ -88,11 +76,10 @@ const FileList = ({
<Text style={{ marginTop: '1px', minWidth: 0 }} ellipsis> <Text style={{ marginTop: '1px', minWidth: 0 }} ellipsis>
{file.name || file.filename || 'Unknown file'} {file.name || file.filename || 'Unknown file'}
</Text> </Text>
{file.extension && <Tag style={{ margin: 0 }}>{file.extension}</Tag>} <Tag>{file.extension}</Tag>
</Flex> </Flex>
{!minimal && (
<Flex gap={'small'} align='center'> <Flex gap={'small'} align='center'>
{showDownload && ( {showDownload && !minimal && (
<Button <Button
icon={<DownloadIcon />} icon={<DownloadIcon />}
size='small' size='small'
@ -100,7 +87,7 @@ const FileList = ({
onClick={() => handleDownload(file)} onClick={() => handleDownload(file)}
/> />
)} )}
{showPreview && ( {showPreview && !minimal && (
<Button <Button
icon={previewOpen ? <EyeSlashIcon /> : <EyeIcon />} icon={previewOpen ? <EyeSlashIcon /> : <EyeIcon />}
size='small' size='small'
@ -114,7 +101,7 @@ const FileList = ({
}} }}
/> />
)} )}
{showInfo && ( {showInfo && !minimal && (
<Button <Button
icon={<InfoCircleIcon />} icon={<InfoCircleIcon />}
size='small' size='small'
@ -124,7 +111,7 @@ const FileList = ({
}} }}
/> />
)} )}
{editing && ( {editing && !minimal && (
<Button <Button
icon={<BinIcon />} icon={<BinIcon />}
size='small' size='small'
@ -133,7 +120,6 @@ const FileList = ({
/> />
)} )}
</Flex> </Flex>
)}
</Flex> </Flex>
{previewOpen && !minimal ? ( {previewOpen && !minimal ? (
<> <>
@ -144,16 +130,8 @@ const FileList = ({
</Flex> </Flex>
) )
const rootStyle = {
minWidth: 0,
maxWidth,
...(card
? { width: '100%' }
: { width: 'fit-content', alignSelf: 'flex-start' })
}
return ( return (
<div style={rootStyle}> <div style={{ width: '100%' }}>
{filesToRender.map((file, index) => { {filesToRender.map((file, index) => {
const key = file._id || file.id || index const key = file._id || file.id || index
const style = { const style = {
@ -198,8 +176,7 @@ FileList.propTypes = {
showDownload: PropTypes.bool, showDownload: PropTypes.bool,
defaultPreviewOpen: PropTypes.bool, defaultPreviewOpen: PropTypes.bool,
card: PropTypes.bool, card: PropTypes.bool,
minimal: PropTypes.bool, minimal: PropTypes.bool
maxWidth: PropTypes.string
} }
export default FileList export default FileList

View File

@ -333,10 +333,10 @@ const ObjectChildTable = ({
canAddRemove, canAddRemove,
itemsSource, itemsSource,
onChange, onChange,
rowKey,
getResolvedRecordKey, getResolvedRecordKey,
handleOpenDetailModal, handleOpenDetailModal,
handleRemoveItem, handleRemoveItem
objectData
]) ])
const skeletonData = useMemo(() => { const skeletonData = useMemo(() => {

View File

@ -616,7 +616,6 @@ const ObjectProperty = ({
minimal={minimal} minimal={minimal}
multiple={false} multiple={false}
card={false} card={false}
maxWidth={maxWidth}
defaultPreviewOpen={previewOpen} defaultPreviewOpen={previewOpen}
showPreview={showPreview} showPreview={showPreview}
showInfo={showHyperlink} showInfo={showHyperlink}
@ -631,7 +630,6 @@ const ObjectProperty = ({
files={value} files={value}
minimal={minimal} minimal={minimal}
multiple={true} multiple={true}
maxWidth={maxWidth}
defaultPreviewOpen={previewOpen} defaultPreviewOpen={previewOpen}
showPreview={showPreview} showPreview={showPreview}
showInfo={showHyperlink} showInfo={showHyperlink}

View File

@ -50,7 +50,7 @@ const logger = loglevel.getLogger('DasboardTable')
logger.setLevel(config.logLevel) logger.setLevel(config.logLevel)
const SCROLL_THRESHOLD = 50 const SCROLL_THRESHOLD = 50
const SKELETON_HEIGHT = 49.5 const SKELETON_HEIGHT = 49
const RowForm = ({ record, isEditing, onRegister, children }) => { const RowForm = ({ record, isEditing, onRegister, children }) => {
const [form] = Form.useForm() const [form] = Form.useForm()
@ -966,11 +966,7 @@ const ObjectTable = forwardRef(
render: (text, record) => { render: (text, record) => {
if (record?.isSkeleton) { if (record?.isSkeleton) {
return ( return (
<Skeleton.Input <Skeleton.Input active size='small' style={{ width: '100%' }} />
active
size='small'
style={{ width: '100%', height: 24.5 }}
/>
) )
} }
return ( return (

View File

@ -53,7 +53,7 @@ const PropertyChanges = ({ type, value }) => {
} }
return ( return (
<Descriptions.Item key={key} label={changeProperty.label}> <Descriptions.Item key={key} label={changeProperty.label}>
<Flex gap={'small'} style={{ minWidth: 0, width: '100%' }}> <Flex gap={'small'}>
{value?.old ? ( {value?.old ? (
<ObjectProperty <ObjectProperty
{...changeProperty} {...changeProperty}

View File

@ -10,7 +10,6 @@ import StopCircleIcon from '../../components/Icons/StopCircleIcon'
import FilamentStockIcon from '../../components/Icons/FilamentStockIcon' import FilamentStockIcon from '../../components/Icons/FilamentStockIcon'
import ControlIcon from '../../components/Icons/ControlIcon' import ControlIcon from '../../components/Icons/ControlIcon'
import JobIcon from '../../components/Icons/JobIcon' import JobIcon from '../../components/Icons/JobIcon'
import PlusIcon from '../../components/Icons/PlusIcon'
export const Printer = { export const Printer = {
name: 'printer', name: 'printer',
@ -68,15 +67,6 @@ export const Printer = {
} }
}, },
{ type: 'divider' }, { type: 'divider' },
{
name: 'newPrinterProfile',
label: 'New Printer Profile',
type: 'button',
icon: PlusIcon,
url: (_id) =>
`/dashboard/production/printers/info?printerId=${_id}&action=newPrinterProfile`
},
{ type: 'divider' },
{ {
name: 'restartSubmenu', name: 'restartSubmenu',
label: 'Restart', label: 'Restart',