Compare commits
6 Commits
6ea5f2848a
...
d982b8dfb3
| Author | SHA1 | Date | |
|---|---|---|---|
| d982b8dfb3 | |||
| a2d27c692a | |||
| e3544ef2e2 | |||
| e52798d4ac | |||
| f3d6bebc38 | |||
| 12cae32e33 |
@ -34,6 +34,7 @@ import AuthCallback from './components/App/AuthCallback.jsx'
|
|||||||
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
||||||
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
||||||
import AuthLaunch from './components/App/AppLaunch.jsx'
|
import AuthLaunch from './components/App/AppLaunch.jsx'
|
||||||
|
import { TableStateProvider } from './components/Dashboard/context/TableStateContext.jsx'
|
||||||
const SlicerIntegration = lazy(
|
const SlicerIntegration = lazy(
|
||||||
() =>
|
() =>
|
||||||
import('./components/Dashboard/Production/Printers/SlicerIntegration.jsx')
|
import('./components/Dashboard/Production/Printers/SlicerIntegration.jsx')
|
||||||
@ -86,6 +87,7 @@ const AppContent = () => {
|
|||||||
<NotificationProvider>
|
<NotificationProvider>
|
||||||
<SpotlightProvider>
|
<SpotlightProvider>
|
||||||
<ActionsModalProvider>
|
<ActionsModalProvider>
|
||||||
|
<TableStateProvider>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path='/applaunch'
|
path='/applaunch'
|
||||||
@ -156,6 +158,7 @@ const AppContent = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</TableStateProvider>
|
||||||
</ActionsModalProvider>
|
</ActionsModalProvider>
|
||||||
</SpotlightProvider>
|
</SpotlightProvider>
|
||||||
</NotificationProvider>
|
</NotificationProvider>
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import DashboardBreadcrumb from './common/DashboardBreadcrumb'
|
|||||||
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
||||||
import { useThemeContext } from './context/ThemeContext'
|
import { useThemeContext } from './context/ThemeContext'
|
||||||
import { MessageProvider } from './context/MessageContext'
|
import { MessageProvider } from './context/MessageContext'
|
||||||
import { TableStateProvider } from './context/TableStateContext'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ const DashboardLayout = ({ children }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageProvider>
|
<MessageProvider>
|
||||||
<TableStateProvider>
|
|
||||||
<Layout
|
<Layout
|
||||||
style={{ height: 'var(--unit-100vh)' }}
|
style={{ height: 'var(--unit-100vh)' }}
|
||||||
className={isDarkMode ? 'dark-mode' : 'light-mode'}
|
className={isDarkMode ? 'dark-mode' : 'light-mode'}
|
||||||
@ -64,7 +62,6 @@ const DashboardLayout = ({ children }) => {
|
|||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</TableStateProvider>
|
|
||||||
</MessageProvider>
|
</MessageProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import LoadFilamentStock from '../../Inventory/FilamentStocks/LoadFilamentStock.
|
|||||||
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock.jsx'
|
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
|
import PrinterControlButtons from '../../common/PrinterControlButtons.jsx'
|
||||||
|
|
||||||
const log = loglevel.getLogger('ControlPrinter')
|
const log = loglevel.getLogger('ControlPrinter')
|
||||||
log.setLevel(config.logLevel)
|
log.setLevel(config.logLevel)
|
||||||
@ -182,6 +183,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
>
|
>
|
||||||
<PrinterTemperaturePanel
|
<PrinterTemperaturePanel
|
||||||
id={printerId}
|
id={printerId}
|
||||||
|
offline={!objectFormState.objectData?.online}
|
||||||
disabled={
|
disabled={
|
||||||
!objectFormState.objectData?.online || objectFormState.loading
|
!objectFormState.objectData?.online || objectFormState.loading
|
||||||
}
|
}
|
||||||
@ -243,6 +245,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
>
|
>
|
||||||
<PrinterMiscPanel
|
<PrinterMiscPanel
|
||||||
id={printerId}
|
id={printerId}
|
||||||
|
offline={!objectFormState.objectData?.online}
|
||||||
disabled={
|
disabled={
|
||||||
!objectFormState.objectData?.online || objectFormState.loading
|
!objectFormState.objectData?.online || objectFormState.loading
|
||||||
}
|
}
|
||||||
@ -272,7 +275,8 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
visibleActions={{
|
visibleActions={{
|
||||||
edit: false,
|
edit: false,
|
||||||
info: !slicerIntegration,
|
info: !slicerIntegration,
|
||||||
control: !slicerIntegration
|
control: !slicerIntegration,
|
||||||
|
newPrinterProfile: !slicerIntegration
|
||||||
}}
|
}}
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
/>
|
/>
|
||||||
@ -318,11 +322,26 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
|
<Flex gap='small'>
|
||||||
|
<PrinterControlButtons
|
||||||
|
callAction={(action) =>
|
||||||
|
actionHandlerRef.current?.callAction(action)
|
||||||
|
}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={
|
||||||
|
!objectFormState.objectData?.online || objectFormState.loading
|
||||||
|
}
|
||||||
|
loading={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
{!slicerIntegration && (
|
||||||
<ObjectTableNavigationButtons
|
<ObjectTableNavigationButtons
|
||||||
|
showEndingDivider={true}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
_id={printerId}
|
_id={printerId}
|
||||||
objectType='printer'
|
objectType='printer'
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<AlertsDisplay
|
<AlertsDisplay
|
||||||
|
|||||||
@ -36,8 +36,6 @@ const FilePreview = ({ file, style = {} }) => {
|
|||||||
}
|
}
|
||||||
}, [file._id, file?.type, fetchPreview, token])
|
}, [file._id, file?.type, fetchPreview, token])
|
||||||
|
|
||||||
console.log(file)
|
|
||||||
|
|
||||||
if (loading == true || !file?.type) {
|
if (loading == true || !file?.type) {
|
||||||
return <LoadingPlaceholder message={'Loading file preview...'} />
|
return <LoadingPlaceholder message={'Loading file preview...'} />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { useEffect, useRef, cloneElement, useMemo } from 'react'
|
import { useEffect, useRef, cloneElement, useMemo } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Popover, Typography } from 'antd'
|
import { Popover, Typography } from 'antd'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../config'
|
||||||
|
const logger = loglevel.getLogger('ApiServerContext')
|
||||||
|
logger.setLevel(config.logLevel)
|
||||||
|
|
||||||
const MODIFIER_ALIASES = {
|
const MODIFIER_ALIASES = {
|
||||||
cmd: 'meta',
|
cmd: 'meta',
|
||||||
@ -59,7 +63,9 @@ const KeyboardShortcut = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (log) {
|
if (log) {
|
||||||
console.log('Key Down:', key, 'Pressed Keys:', [...pressedKeysRef.current])
|
logger.info('Key Down:', key, 'Pressed Keys:', [
|
||||||
|
...pressedKeysRef.current
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -37,9 +37,48 @@ function filterActionsByVisibility(actions, visibleActions) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanDividers(items) {
|
||||||
|
if (!Array.isArray(items) || items.length === 0) return []
|
||||||
|
|
||||||
|
const cleaned = []
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (!item) continue
|
||||||
|
|
||||||
|
if (item.type === 'divider') {
|
||||||
|
if (cleaned.length === 0) continue
|
||||||
|
if (cleaned[cleaned.length - 1]?.type === 'divider') continue
|
||||||
|
cleaned.push(item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children && Array.isArray(item.children)) {
|
||||||
|
const children = cleanDividers(item.children)
|
||||||
|
if (children.length === 0) continue
|
||||||
|
cleaned.push({ ...item, children })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cleaned.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleaned[cleaned.length - 1]?.type === 'divider') {
|
||||||
|
cleaned.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively map actions to AntD Dropdown items
|
// Recursively map actions to AntD Dropdown items
|
||||||
function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
function mapActionsToMenuItems(
|
||||||
return actions.map((action) => {
|
actions,
|
||||||
|
currentUrlWithActions,
|
||||||
|
id,
|
||||||
|
objectData,
|
||||||
|
userProfile
|
||||||
|
) {
|
||||||
|
return cleanDividers(
|
||||||
|
actions.map((action) => {
|
||||||
if (action.type === 'divider') {
|
if (action.type === 'divider') {
|
||||||
return { type: 'divider' }
|
return { type: 'divider' }
|
||||||
}
|
}
|
||||||
@ -48,8 +87,6 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
|||||||
var disabled = actionUrl && actionUrl === currentUrlWithActions
|
var disabled = actionUrl && actionUrl === currentUrlWithActions
|
||||||
var visible = true
|
var visible = true
|
||||||
|
|
||||||
const { userProfile } = useContext(AuthContext)
|
|
||||||
|
|
||||||
if (action.disabled) {
|
if (action.disabled) {
|
||||||
if (typeof action.disabled === 'function') {
|
if (typeof action.disabled === 'function') {
|
||||||
disabled = action.disabled({ ...objectData, _user: userProfile })
|
disabled = action.disabled({ ...objectData, _user: userProfile })
|
||||||
@ -66,6 +103,10 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visible != true) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
key: action.key || action.name,
|
key: action.key || action.name,
|
||||||
label: action.label,
|
label: action.label,
|
||||||
@ -78,13 +119,13 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
|||||||
action.children,
|
action.children,
|
||||||
currentUrlWithActions,
|
currentUrlWithActions,
|
||||||
id,
|
id,
|
||||||
objectData
|
objectData,
|
||||||
|
userProfile
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (visible == true) {
|
|
||||||
return item
|
return item
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const stripActionParam = (pathname, search) => {
|
const stripActionParam = (pathname, search) => {
|
||||||
@ -108,6 +149,7 @@ const ObjectActions = ({
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const { showActionsModal } = useActionsModal()
|
const { showActionsModal } = useActionsModal()
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
// Get current url without 'action' param
|
// Get current url without 'action' param
|
||||||
const currentUrlWithoutActions = stripActionParam(
|
const currentUrlWithoutActions = stripActionParam(
|
||||||
location.pathname,
|
location.pathname,
|
||||||
@ -120,11 +162,14 @@ const ObjectActions = ({
|
|||||||
visibleActions
|
visibleActions
|
||||||
)
|
)
|
||||||
|
|
||||||
const filteredActions = visibilityFilteredActions.filter(
|
const filteredActions = cleanDividers(
|
||||||
|
visibilityFilteredActions.filter(
|
||||||
(action) =>
|
(action) =>
|
||||||
|
action.type === 'divider' ||
|
||||||
typeof action.url !== 'function' ||
|
typeof action.url !== 'function' ||
|
||||||
action.url(id) !== currentUrlWithoutActions
|
action.url(id) !== currentUrlWithoutActions
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const currentUrlWithActions = location.pathname + location.search
|
const currentUrlWithActions = location.pathname + location.search
|
||||||
|
|
||||||
@ -134,7 +179,8 @@ const ObjectActions = ({
|
|||||||
filteredActions,
|
filteredActions,
|
||||||
currentUrlWithActions,
|
currentUrlWithActions,
|
||||||
id,
|
id,
|
||||||
objectData
|
objectData,
|
||||||
|
userProfile
|
||||||
),
|
),
|
||||||
onClick: (info) => {
|
onClick: (info) => {
|
||||||
// Find the action by key
|
// Find the action by key
|
||||||
|
|||||||
@ -127,8 +127,6 @@ const ObjectForm = forwardRef(
|
|||||||
searchParams.delete(actionParam)
|
searchParams.delete(actionParam)
|
||||||
const newSearch = searchParams.toString()
|
const newSearch = searchParams.toString()
|
||||||
|
|
||||||
console.log('newSearch', newSearch)
|
|
||||||
console.log('location.pathname', location.pathname)
|
|
||||||
const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
|
const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
|
||||||
navigate(newPath, { replace: true })
|
navigate(newPath, { replace: true })
|
||||||
},
|
},
|
||||||
@ -537,7 +535,7 @@ const ObjectForm = forwardRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const activityUnsubscribe = subscribeToObjectActivity(
|
const activityUnsubscribe = subscribeToObjectActivity(
|
||||||
id,
|
id?.toLowerCase(),
|
||||||
type,
|
type,
|
||||||
activityHandler
|
activityHandler
|
||||||
)
|
)
|
||||||
@ -565,7 +563,7 @@ const ObjectForm = forwardRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
|
const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
|
||||||
id,
|
id?.toLowerCase(),
|
||||||
type,
|
type,
|
||||||
objectUpdateHandler
|
objectUpdateHandler
|
||||||
)
|
)
|
||||||
|
|||||||
@ -693,7 +693,7 @@ const ObjectTable = forwardRef(
|
|||||||
// Subscribe to new items only
|
// Subscribe to new items only
|
||||||
newItemIds.forEach((itemId) => {
|
newItemIds.forEach((itemId) => {
|
||||||
const unsubscribe = subscribeToObjectUpdates(
|
const unsubscribe = subscribeToObjectUpdates(
|
||||||
itemId,
|
itemId?.toLowerCase(),
|
||||||
type,
|
type,
|
||||||
(updateData) => {
|
(updateData) => {
|
||||||
updateEventHandlerRef.current(itemId, updateData)
|
updateEventHandlerRef.current(itemId, updateData)
|
||||||
|
|||||||
89
src/components/Dashboard/common/PrinterControlButtons.jsx
Normal file
89
src/components/Dashboard/common/PrinterControlButtons.jsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { createElement, useContext } from 'react'
|
||||||
|
import { Button, Space } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
import PlayCircleIcon from '../../Icons/PlayCircleIcon'
|
||||||
|
import PauseCircleIcon from '../../Icons/PauseCircleIcon'
|
||||||
|
import StopCircleIcon from '../../Icons/StopCircleIcon'
|
||||||
|
|
||||||
|
const findAction = (actions, name) => {
|
||||||
|
for (const action of actions) {
|
||||||
|
if (action.name === name) return action
|
||||||
|
if (action.children) {
|
||||||
|
const found = findAction(action.children, name)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActionDisabled = (action, objectData, userProfile, disabled) => {
|
||||||
|
if (disabled) return true
|
||||||
|
if (!action?.disabled) return false
|
||||||
|
if (typeof action.disabled === 'function') {
|
||||||
|
return action.disabled({ ...objectData, _user: userProfile })
|
||||||
|
}
|
||||||
|
return action.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
const PrinterControlButtons = ({ callAction, objectData, disabled }) => {
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
|
const model = getModelByName('printer')
|
||||||
|
const startQueueAction = findAction(model.actions, 'startQueue')
|
||||||
|
const pauseJobAction = findAction(model.actions, 'pauseJob')
|
||||||
|
const resumeJobAction = findAction(model.actions, 'resumeJob')
|
||||||
|
const cancelJobAction = findAction(model.actions, 'cancelJob')
|
||||||
|
|
||||||
|
const isPaused = objectData?.state?.type === 'paused'
|
||||||
|
const startAction = isPaused ? resumeJobAction : startQueueAction
|
||||||
|
const startActionName = isPaused ? 'resumeJob' : 'startQueue'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space size='small'>
|
||||||
|
<Button
|
||||||
|
icon={createElement(PlayCircleIcon)}
|
||||||
|
onClick={() => callAction(startActionName)}
|
||||||
|
disabled={isActionDisabled(
|
||||||
|
startAction,
|
||||||
|
objectData,
|
||||||
|
userProfile,
|
||||||
|
disabled
|
||||||
|
)}
|
||||||
|
title={startAction?.label}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={createElement(PauseCircleIcon)}
|
||||||
|
onClick={() => callAction('pauseJob')}
|
||||||
|
disabled={isActionDisabled(
|
||||||
|
pauseJobAction,
|
||||||
|
objectData,
|
||||||
|
userProfile,
|
||||||
|
disabled
|
||||||
|
)}
|
||||||
|
title={pauseJobAction?.label}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={createElement(StopCircleIcon)}
|
||||||
|
onClick={() => callAction('cancelJob')}
|
||||||
|
disabled={isActionDisabled(
|
||||||
|
cancelJobAction,
|
||||||
|
objectData,
|
||||||
|
userProfile,
|
||||||
|
disabled
|
||||||
|
)}
|
||||||
|
title={cancelJobAction?.label}
|
||||||
|
danger
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PrinterControlButtons.propTypes = {
|
||||||
|
callAction: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object.isRequired,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
loading: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrinterControlButtons
|
||||||
@ -7,7 +7,12 @@ import merge from 'lodash/merge'
|
|||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const PrinterMiscPanel = ({ id, showControls = true, disabled = false }) => {
|
const PrinterMiscPanel = ({
|
||||||
|
id,
|
||||||
|
showControls = true,
|
||||||
|
disabled = false,
|
||||||
|
offline = true
|
||||||
|
}) => {
|
||||||
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
||||||
useContext(ApiServerContext)
|
useContext(ApiServerContext)
|
||||||
const controlsDisabled = disabled || !connected
|
const controlsDisabled = disabled || !connected
|
||||||
@ -35,10 +40,22 @@ const PrinterMiscPanel = ({ id, showControls = true, disabled = false }) => {
|
|||||||
const [lcdBrightness, setLcdBrightness] = useState(0)
|
const [lcdBrightness, setLcdBrightness] = useState(0)
|
||||||
const [beeperValue, setBeeperValue] = useState(0)
|
const [beeperValue, setBeeperValue] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (offline) {
|
||||||
|
setMiscData({
|
||||||
|
fan: { speed: 0, target: 0 },
|
||||||
|
lcdBacklight: { brightness: 0 },
|
||||||
|
beeper: { value: 0 },
|
||||||
|
filamentSensor: { enabled: false, filamentDetected: false },
|
||||||
|
coolingFan: { speed: 0 }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [offline])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && connected == true) {
|
if (id && connected == true) {
|
||||||
const miscEventUnsubscribe = subscribeToObjectEvent(
|
const miscEventUnsubscribe = subscribeToObjectEvent(
|
||||||
id,
|
id?.toLowerCase(),
|
||||||
'printer',
|
'printer',
|
||||||
'misc',
|
'misc',
|
||||||
(event) => {
|
(event) => {
|
||||||
@ -193,7 +210,8 @@ const PrinterMiscPanel = ({ id, showControls = true, disabled = false }) => {
|
|||||||
PrinterMiscPanel.propTypes = {
|
PrinterMiscPanel.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
showControls: PropTypes.bool,
|
showControls: PropTypes.bool,
|
||||||
disabled: PropTypes.bool
|
disabled: PropTypes.bool,
|
||||||
|
offline: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterMiscPanel
|
export default PrinterMiscPanel
|
||||||
|
|||||||
@ -30,11 +30,23 @@ const CustomCollapse = styled(Collapse)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const controlLabelStyle = {
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
flexShrink: 0,
|
||||||
|
minWidth: '120px'
|
||||||
|
}
|
||||||
|
|
||||||
|
const controlInputStyle = {
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0
|
||||||
|
}
|
||||||
|
|
||||||
const PrinterPositionPanel = ({
|
const PrinterPositionPanel = ({
|
||||||
id,
|
id,
|
||||||
showControls = true,
|
showControls = true,
|
||||||
showMoreInfo = true,
|
showMoreInfo = true,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
offline = true
|
||||||
}) => {
|
}) => {
|
||||||
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
||||||
useContext(ApiServerContext)
|
useContext(ApiServerContext)
|
||||||
@ -55,10 +67,30 @@ const PrinterPositionPanel = ({
|
|||||||
minCruiseRatio: 0
|
minCruiseRatio: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (offline) {
|
||||||
|
setPositionData({
|
||||||
|
speedFactor: 1.0,
|
||||||
|
speed: 100,
|
||||||
|
extrudeFactor: 1.0,
|
||||||
|
absoluteCoordinates: true,
|
||||||
|
absoluteExtrude: false,
|
||||||
|
homingOrigin: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
toolheadPosition: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
gcodePosition: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
livePosition: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
maxVelocity: 1000,
|
||||||
|
maxAcceleration: 1000,
|
||||||
|
squareCornerVelocity: 100,
|
||||||
|
minCruiseRatio: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [offline])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && connected == true) {
|
if (id && connected == true) {
|
||||||
const motionEventUnsubscribe = subscribeToObjectEvent(
|
const motionEventUnsubscribe = subscribeToObjectEvent(
|
||||||
id,
|
id?.toLowerCase(),
|
||||||
'printer',
|
'printer',
|
||||||
'motion',
|
'motion',
|
||||||
(event) => {
|
(event) => {
|
||||||
@ -73,6 +105,7 @@ const PrinterPositionPanel = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [id, connected, subscribeToObjectEvent])
|
}, [id, connected, subscribeToObjectEvent])
|
||||||
|
|
||||||
const [speedFactor, setSpeedFactor] = useState(positionData.speedFactor)
|
const [speedFactor, setSpeedFactor] = useState(positionData.speedFactor)
|
||||||
const [extrudeFactor, setExtrudeFactor] = useState(positionData.extrudeFactor)
|
const [extrudeFactor, setExtrudeFactor] = useState(positionData.extrudeFactor)
|
||||||
const [maxVelocity, setMaxVelocity] = useState(positionData.maxVelocity)
|
const [maxVelocity, setMaxVelocity] = useState(positionData.maxVelocity)
|
||||||
@ -212,16 +245,22 @@ const PrinterPositionPanel = ({
|
|||||||
</Descriptions>
|
</Descriptions>
|
||||||
{showControls && (
|
{showControls && (
|
||||||
<>
|
<>
|
||||||
<Space direction='vertical' style={{ width: '100%' }}>
|
<Flex vertical gap='small' style={{ width: '100%' }}>
|
||||||
<Space direction='horizontal'>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Text>Max Velocity:</Text>
|
<Text style={{ ...controlLabelStyle, minWidth: '91px' }}>
|
||||||
<Space.Compact block size='small'>
|
Max Velocity:
|
||||||
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(maxVelocity, 2)}
|
value={round(maxVelocity, 2)}
|
||||||
min={1}
|
min={1}
|
||||||
max={10000}
|
max={10000}
|
||||||
step={5}
|
step={5}
|
||||||
style={{ width: '125px' }}
|
style={controlInputStyle}
|
||||||
suffix='mm/s'
|
suffix='mm/s'
|
||||||
onChange={(value) => setMaxVelocity(value)}
|
onChange={(value) => setMaxVelocity(value)}
|
||||||
onPressEnter={handleSetMaxVelocity}
|
onPressEnter={handleSetMaxVelocity}
|
||||||
@ -237,18 +276,23 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
|
|
||||||
<Space direction='vertical' style={{ width: '100%' }}>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Space direction='horizontal'>
|
<Text style={{ ...controlLabelStyle, minWidth: '120px' }}>
|
||||||
<Text>Max Acceleration:</Text>
|
Max Acceleration:
|
||||||
<Space.Compact block size='small'>
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(maxAcceleration, 2)}
|
value={round(maxAcceleration, 2)}
|
||||||
min={1}
|
min={1}
|
||||||
max={10000}
|
max={10000}
|
||||||
step={5}
|
step={5}
|
||||||
style={{ width: '125px' }}
|
style={controlInputStyle}
|
||||||
suffix='mm/s²'
|
suffix='mm/s²'
|
||||||
onChange={(value) => setMaxAcceleration(value)}
|
onChange={(value) => setMaxAcceleration(value)}
|
||||||
onPressEnter={handleSetMaxAcceleration}
|
onPressEnter={handleSetMaxAcceleration}
|
||||||
@ -264,20 +308,24 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Space direction='vertical' style={{ width: '100%' }}>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Space direction='horizontal'>
|
<Text style={{ ...controlLabelStyle, minWidth: '102px' }}>
|
||||||
<Text>Sqr Corner Vel:</Text>
|
Sqr Corner Vel:
|
||||||
<Space.Compact block size='small'>
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(squareCornerVelocity, 2) || 0}
|
value={round(squareCornerVelocity, 2) || 0}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={1000}
|
max={1000}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
|
style={controlInputStyle}
|
||||||
suffix='mm/s'
|
suffix='mm/s'
|
||||||
style={{ width: '125px' }}
|
|
||||||
onChange={(value) => setSquareCornerVelocity(value)}
|
onChange={(value) => setSquareCornerVelocity(value)}
|
||||||
onPressEnter={handleSetSquareCornerVelocity}
|
onPressEnter={handleSetSquareCornerVelocity}
|
||||||
size='small'
|
size='small'
|
||||||
@ -292,20 +340,24 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Space direction='vertical' style={{ width: '100%' }}>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Space direction='horizontal'>
|
<Text style={{ ...controlLabelStyle, minWidth: '114px' }}>
|
||||||
<Text>Min Cruise Ratio:</Text>
|
Min Cruise Ratio:
|
||||||
<Space.Compact block size='small'>
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(minCruiseRatio * 100, 2) || 0}
|
value={round(minCruiseRatio * 100, 2) || 0}
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
step={1}
|
step={1}
|
||||||
suffix='%'
|
suffix='%'
|
||||||
style={{ width: '125px' }}
|
style={controlInputStyle}
|
||||||
onChange={(value) => setMinCruiseRatio(value / 100)}
|
onChange={(value) => setMinCruiseRatio(value / 100)}
|
||||||
onPressEnter={handleSetMinCruiseRatio}
|
onPressEnter={handleSetMinCruiseRatio}
|
||||||
size='small'
|
size='small'
|
||||||
@ -320,9 +372,8 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
</Space>
|
</Flex>
|
||||||
</Space>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Text>Homing Origin:</Text>
|
<Text>Homing Origin:</Text>
|
||||||
@ -398,16 +449,22 @@ const PrinterPositionPanel = ({
|
|||||||
</Descriptions>
|
</Descriptions>
|
||||||
{showControls && (
|
{showControls && (
|
||||||
<>
|
<>
|
||||||
<Space direction='vertical' style={{ width: '100%' }}>
|
<Flex vertical gap='small' style={{ width: '100%' }}>
|
||||||
<Space direction='horizontal'>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Text>Speed Factor:</Text>
|
<Text style={{ ...controlLabelStyle, minWidth: '96px' }}>
|
||||||
<Space.Compact block size='small'>
|
Speed Factor:
|
||||||
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(speedFactor, 2)}
|
value={round(speedFactor, 2)}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={2}
|
max={2}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{ width: '125px' }}
|
style={controlInputStyle}
|
||||||
suffix='%'
|
suffix='%'
|
||||||
onChange={(value) => setSpeedFactor(value)}
|
onChange={(value) => setSpeedFactor(value)}
|
||||||
onPressEnter={handleSetSpeedFactor}
|
onPressEnter={handleSetSpeedFactor}
|
||||||
@ -423,17 +480,23 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
|
|
||||||
<Space direction='horizontal'>
|
<Flex align='center' style={{ width: '100%' }}>
|
||||||
<Text>Extrude Factor:</Text>
|
<Text style={{ ...controlLabelStyle, minWidth: '105px' }}>
|
||||||
<Space.Compact block size='small'>
|
Extrude Factor:
|
||||||
|
</Text>
|
||||||
|
<Space.Compact
|
||||||
|
block
|
||||||
|
size='small'
|
||||||
|
style={{ flex: 1, minWidth: 0 }}
|
||||||
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={round(extrudeFactor, 2)}
|
value={round(extrudeFactor, 2)}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={2}
|
max={2}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{ width: '125px' }}
|
style={controlInputStyle}
|
||||||
suffix='%'
|
suffix='%'
|
||||||
onChange={(value) => setExtrudeFactor(value)}
|
onChange={(value) => setExtrudeFactor(value)}
|
||||||
onPressEnter={handleSetExtrudeFactor}
|
onPressEnter={handleSetExtrudeFactor}
|
||||||
@ -449,8 +512,8 @@ const PrinterPositionPanel = ({
|
|||||||
Set
|
Set
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
</Space>
|
</Flex>
|
||||||
</Space>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Descriptions
|
<Descriptions
|
||||||
@ -509,7 +572,8 @@ PrinterPositionPanel.propTypes = {
|
|||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
showControls: PropTypes.bool,
|
showControls: PropTypes.bool,
|
||||||
showMoreInfo: PropTypes.bool,
|
showMoreInfo: PropTypes.bool,
|
||||||
disabled: PropTypes.bool
|
disabled: PropTypes.bool,
|
||||||
|
offline: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterPositionPanel
|
export default PrinterPositionPanel
|
||||||
|
|||||||
@ -38,7 +38,8 @@ const PrinterTemperaturePanel = ({
|
|||||||
showExtruder = true,
|
showExtruder = true,
|
||||||
showBed = true,
|
showBed = true,
|
||||||
showMoreInfo = true,
|
showMoreInfo = true,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
offline = true
|
||||||
}) => {
|
}) => {
|
||||||
const [temperatureData, setTemperatureData] = useState({
|
const [temperatureData, setTemperatureData] = useState({
|
||||||
extruder: {
|
extruder: {
|
||||||
@ -55,6 +56,17 @@ const PrinterTemperaturePanel = ({
|
|||||||
ambiant: 0
|
ambiant: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (offline) {
|
||||||
|
setTemperatureData({
|
||||||
|
extruder: { current: 0, target: 0, power: 0 },
|
||||||
|
bed: { current: 0, target: 0, power: 0 },
|
||||||
|
pinda: 0,
|
||||||
|
ambiant: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [offline])
|
||||||
|
|
||||||
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
||||||
useContext(ApiServerContext)
|
useContext(ApiServerContext)
|
||||||
const controlsDisabled = disabled || !connected
|
const controlsDisabled = disabled || !connected
|
||||||
@ -72,9 +84,9 @@ const PrinterTemperaturePanel = ({
|
|||||||
}, [temperatureData.bed?.target])
|
}, [temperatureData.bed?.target])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && connected == true) {
|
if (id && connected == true && offline == false) {
|
||||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
||||||
id,
|
id?.toLowerCase(),
|
||||||
'printer',
|
'printer',
|
||||||
'temperature',
|
'temperature',
|
||||||
(event) => {
|
(event) => {
|
||||||
@ -88,7 +100,7 @@ const PrinterTemperaturePanel = ({
|
|||||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [id, connected, subscribeToObjectEvent])
|
}, [id, connected, subscribeToObjectEvent, offline])
|
||||||
|
|
||||||
const [extruderTarget, setExtruderTarget] = useState(0)
|
const [extruderTarget, setExtruderTarget] = useState(0)
|
||||||
const [bedTarget, setBedTarget] = useState(0)
|
const [bedTarget, setBedTarget] = useState(0)
|
||||||
@ -177,7 +189,7 @@ const PrinterTemperaturePanel = ({
|
|||||||
value={extruderTarget}
|
value={extruderTarget}
|
||||||
min={0}
|
min={0}
|
||||||
max={300}
|
max={300}
|
||||||
style={{ width: '120px' }}
|
style={{ width: '140px' }}
|
||||||
addonAfter='°C'
|
addonAfter='°C'
|
||||||
onChange={(value) => setExtruderTarget(value || 0)}
|
onChange={(value) => setExtruderTarget(value || 0)}
|
||||||
onPressEnter={() =>
|
onPressEnter={() =>
|
||||||
@ -241,7 +253,7 @@ const PrinterTemperaturePanel = ({
|
|||||||
value={bedTarget}
|
value={bedTarget}
|
||||||
min={0}
|
min={0}
|
||||||
max={300}
|
max={300}
|
||||||
style={{ width: '120px' }}
|
style={{ width: '140px' }}
|
||||||
addonAfter='°C'
|
addonAfter='°C'
|
||||||
onChange={(value) => setBedTarget(value || 0)}
|
onChange={(value) => setBedTarget(value || 0)}
|
||||||
onPressEnter={() =>
|
onPressEnter={() =>
|
||||||
@ -309,7 +321,8 @@ PrinterTemperaturePanel.propTypes = {
|
|||||||
showBed: PropTypes.bool,
|
showBed: PropTypes.bool,
|
||||||
showMoreInfo: PropTypes.bool,
|
showMoreInfo: PropTypes.bool,
|
||||||
shouldUnsubscribe: PropTypes.bool,
|
shouldUnsubscribe: PropTypes.bool,
|
||||||
disabled: PropTypes.bool
|
disabled: PropTypes.bool,
|
||||||
|
offline: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterTemperaturePanel
|
export default PrinterTemperaturePanel
|
||||||
|
|||||||
@ -42,7 +42,6 @@ const Thumbnail = function Thumbnail({
|
|||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
border: 'none',
|
|
||||||
...style
|
...style
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +150,7 @@ const Thumbnail = function Thumbnail({
|
|||||||
<Card
|
<Card
|
||||||
className={className}
|
className={className}
|
||||||
style={cardStyle}
|
style={cardStyle}
|
||||||
styles={{ body: { padding: 0, height: '100%' } }}
|
styles={{ body: { padding: 0, height: '100%', width: style?.width } }}
|
||||||
>
|
>
|
||||||
<Flex justify='center' align='center' style={{ height: '100%' }}>
|
<Flex justify='center' align='center' style={{ height: '100%' }}>
|
||||||
{fallback || (
|
{fallback || (
|
||||||
|
|||||||
@ -1510,7 +1510,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchFileThumbnail = async (file, size) => {
|
const fetchFileThumbnail = async (file, size, show404Error = false) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${config.backendUrl}/files/${file._id}/thumbnail`,
|
`${config.backendUrl}/files/${file._id}/thumbnail`,
|
||||||
@ -1528,6 +1528,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
return window.URL.createObjectURL(blob)
|
return window.URL.createObjectURL(blob)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (err.response.status === 404 && show404Error == false) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
console.error(err)
|
console.error(err)
|
||||||
showError(err, () => {
|
showError(err, () => {
|
||||||
fetchFileThumbnail(file, size)
|
fetchFileThumbnail(file, size)
|
||||||
|
|||||||
@ -164,6 +164,7 @@ export const GCodeFile = {
|
|||||||
showPreview: false,
|
showPreview: false,
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
masterFilter: ['.gcode', '.g'],
|
masterFilter: ['.gcode', '.g'],
|
||||||
|
thumbnail: true,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user