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

View File

@ -1,6 +1,7 @@
import { useMemo } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import PropTypes from 'prop-types'
import NewObjectForm from '../../common/NewObjectForm'
import ObjectForm from '../../common/ObjectForm'
import ObjectInfo from '../../common/ObjectInfo'
import WizardView from '../../common/WizardView'
@ -143,140 +144,229 @@ const SUMMARY_PROPERTIES = [
...OPTIONAL_PROPERTIES
]
const NewPrinterProfile = ({ onOk, reset, defaultValues = {} }) => {
const mergedDefaultValues = useMemo(
() => ({
...PRUSA_MK3S_PROFILE_DEFAULTS,
...defaultValues
}),
[defaultValues]
const PrinterProfileWizard = ({
objectData,
formValid,
loading,
onSubmit,
editing,
printerId = null
}) => {
const visibleProperties = useMemo(() => {
if (!printerId) return undefined
return { printer: false, 'printer._id': false }
}, [printerId])
const steps = useMemo(
() => [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='printerProfile'
properties={REQUIRED_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='75px'
required={true}
objectData={objectData}
visibleProperties={visibleProperties}
/>
)
},
{
title: 'Dimensions',
key: 'dimensions',
content: (
<ObjectInfo
type='printerProfile'
properties={DIMENSION_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='190px'
objectData={objectData}
/>
)
},
{
title: 'Extruders',
key: 'extruders',
content: (
<ObjectInfo
type='printerProfile'
properties={EXTRUDER_STEP_PROPERTIES}
column={1}
bordered={false}
isEditing
showLabels={false}
labelWidth='90px'
objectData={objectData}
/>
)
},
{
title: 'Motion',
key: 'motion',
content: (
<ObjectInfo
type='printerProfile'
properties={MOTION_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='185px'
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='printerProfile'
properties={OPTIONAL_PROPERTIES}
column={1}
labelWidth='170px'
bordered={false}
isEditing
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='printerProfile'
properties={SUMMARY_PROPERTIES}
column={1}
bordered={false}
isEditing={false}
labelWidth='210px'
objectData={objectData}
visibleProperties={visibleProperties}
/>
)
}
],
[objectData, visibleProperties]
)
return (
<NewObjectForm
type='printerProfile'
reset={reset}
defaultValues={mergedDefaultValues}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='printerProfile'
properties={REQUIRED_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='75px'
required={true}
objectData={objectData}
/>
)
},
{
title: 'Dimensions',
key: 'dimensions',
content: (
<ObjectInfo
type='printerProfile'
properties={DIMENSION_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='190px'
objectData={objectData}
/>
)
},
{
title: 'Extruders',
key: 'extruders',
content: (
<ObjectInfo
type='printerProfile'
properties={EXTRUDER_STEP_PROPERTIES}
column={1}
bordered={false}
isEditing
showLabels={false}
labelWidth='90px'
objectData={objectData}
/>
)
},
{
title: 'Motion',
key: 'motion',
content: (
<ObjectInfo
type='printerProfile'
properties={MOTION_PROPERTIES}
column={1}
bordered={false}
isEditing
labelWidth='185px'
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='printerProfile'
properties={OPTIONAL_PROPERTIES}
column={1}
labelWidth='170px'
bordered={false}
isEditing
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='printerProfile'
properties={SUMMARY_PROPERTIES}
column={1}
bordered={false}
isEditing={false}
labelWidth='210px'
objectData={objectData}
/>
)
}
]
return (
<WizardView
sizeBarWidth='160px'
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Printer Profile'
submitText='Create'
onSubmit={async () => {
const result = await handleSubmit()
if (result?._id) onOk(result)
}}
/>
)
}}
</NewObjectForm>
<WizardView
sizeBarWidth='160px'
steps={steps}
loading={loading}
formValid={formValid}
title={editing ? 'Edit Printer Profile' : 'New Printer Profile'}
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 () => {
const result = await handleSubmit()
if (result?._id) onOk(result)
}}
/>
)}
</NewObjectForm>
)
NewPrinterProfile.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
printerId: PropTypes.string
}
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

View File

@ -1,7 +1,9 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card, Modal } from 'antd'
import NewPrinterProfile from '../PrinterProfiles/NewPrinterProfile'
import { Space, Flex, Card, Button, Modal } from 'antd'
import NewPrinterProfile, {
EditPrinterProfile
} from '../PrinterProfiles/NewPrinterProfile'
import { LoadingOutlined } from '@ant-design/icons'
import loglevel from 'loglevel'
import config from '../../../../config.js'
@ -23,6 +25,7 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import PlusIcon from '../../../Icons/PlusIcon.jsx'
import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
const log = loglevel.getLogger('PrinterInfo')
log.setLevel(config.logLevel)
@ -49,7 +52,15 @@ const PrinterInfo = () => {
loading: false,
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 = {
edit: () => {
@ -63,10 +74,6 @@ const PrinterInfo = () => {
finishEdit: () => {
objectFormRef?.current.handleUpdate()
return true
},
newPrinterProfile: () => {
setNewPrinterProfileOpen(true)
return true
}
}
@ -189,15 +196,37 @@ const PrinterInfo = () => {
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
ref={profilesTableRef}
type='printerProfile'
masterFilter={{ printer: printerId }}
visibleColumns={{
printer: false,
'printer._id': false
}}
/>
<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>
@ -236,25 +265,25 @@ const PrinterInfo = () => {
</ScrollBox>
</Flex>
<Modal
open={newPrinterProfileOpen}
styles={{ content: { paddingBottom: '24px' } }}
open={profileModal.open}
footer={null}
width={800}
onCancel={() => {
setNewPrinterProfileOpen(false)
}}
destroyOnHidden={true}
width={1000}
onCancel={() => setProfileModal({ open: false, profileId: null })}
destroyOnHidden
>
<NewPrinterProfile
onOk={() => {
setNewPrinterProfileOpen(false)
profilesTableRef.current?.reload()
}}
reset={newPrinterProfileOpen}
defaultValues={{
printer: { ...objectFormState.objectData }
}}
/>
{profileModal.open &&
(profileModal.profileId ? (
<EditPrinterProfile
profileId={profileModal.profileId}
printerId={printerId}
onOk={handleProfileSaved}
/>
) : (
<NewPrinterProfile
printerId={printerId}
onOk={handleProfileSaved}
/>
))}
</Modal>
</>
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ import StopCircleIcon from '../../components/Icons/StopCircleIcon'
import FilamentStockIcon from '../../components/Icons/FilamentStockIcon'
import ControlIcon from '../../components/Icons/ControlIcon'
import JobIcon from '../../components/Icons/JobIcon'
import PlusIcon from '../../components/Icons/PlusIcon'
export const Printer = {
name: 'printer',
@ -68,15 +67,6 @@ export const Printer = {
}
},
{ 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',
label: 'Restart',