Compare commits

..

No commits in common. "d982b8dfb362a7311322cf9941c69ae626c77ccd" and "6ea5f2848ad7824cf5143c3bb0529845a1bbade8" have entirely different histories.

15 changed files with 277 additions and 531 deletions

View File

@ -34,7 +34,6 @@ 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')
@ -87,78 +86,76 @@ const AppContent = () => {
<NotificationProvider> <NotificationProvider>
<SpotlightProvider> <SpotlightProvider>
<ActionsModalProvider> <ActionsModalProvider>
<TableStateProvider> <Routes>
<Routes> <Route
<Route path='/applaunch'
path='/applaunch' element={<AuthLaunch />}
element={<AuthLaunch />} />
/> <Route
<Route path='/dashboard/electron/spotlightcontent'
path='/dashboard/electron/spotlightcontent' element={
element={ <PrivateRoute
<PrivateRoute component={() => (
component={() => ( <ElectronSpotlightContentPage />
<ElectronSpotlightContentPage /> )}
)} />
/> }
} />
/> <Route
<Route path='/'
path='/' element={
element={ <PrivateRoute
<PrivateRoute component={() => (
component={() => ( <Navigate
<Navigate to='/dashboard/production/overview'
to='/dashboard/production/overview' replace
replace />
/> )}
)} />
/> }
} />
/> <Route
<Route path='/auth/callback'
path='/auth/callback' element={<AuthCallback />}
element={<AuthCallback />} />
/> <Route
<Route path='/auth/marketplace/callback'
path='/auth/marketplace/callback' element={<MarketplaceAuthCallback />}
element={<MarketplaceAuthCallback />} />
/> <Route
<Route path='/email/notification'
path='/email/notification' element={<EmailNotificationTemplate />}
element={<EmailNotificationTemplate />} />
/> <Route
<Route path='/slicer'
path='/slicer' element={<SlicerIntegration />}
element={<SlicerIntegration />} />
/>
<Route <Route
path='/dashboard' path='/dashboard'
element={ element={
<PrivateRoute <PrivateRoute
component={() => <Dashboard />} component={() => <Dashboard />}
/> />
} }
> >
{ProductionRoutes} {ProductionRoutes}
{InventoryRoutes} {InventoryRoutes}
{FinanceRoutes} {FinanceRoutes}
{SalesRoutes} {SalesRoutes}
{ManagementRoutes} {ManagementRoutes}
{DeveloperRoutes} {DeveloperRoutes}
</Route> </Route>
<Route <Route
path='*' path='*'
element={ element={
<AppError <AppError
message='Error 404. Page not found.' message='Error 404. Page not found.'
showRefresh={false} showRefresh={false}
/> />
} }
/> />
</Routes> </Routes>
</TableStateProvider>
</ActionsModalProvider> </ActionsModalProvider>
</SpotlightProvider> </SpotlightProvider>
</NotificationProvider> </NotificationProvider>

View File

@ -12,6 +12,7 @@ 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
@ -28,40 +29,42 @@ const DashboardLayout = ({ children }) => {
return ( return (
<MessageProvider> <MessageProvider>
<Layout <TableStateProvider>
style={{ height: 'var(--unit-100vh)' }} <Layout
className={isDarkMode ? 'dark-mode' : 'light-mode'} style={{ height: 'var(--unit-100vh)' }}
> className={isDarkMode ? 'dark-mode' : 'light-mode'}
<DashboardNavigation /> >
<Layout> <DashboardNavigation />
{isProduction ? ( <Layout>
<ProductionSidebar /> {isProduction ? (
) : isInventory ? ( <ProductionSidebar />
<InventorySidebar /> ) : isInventory ? (
) : isFinance ? ( <InventorySidebar />
<FinanceSidebar /> ) : isFinance ? (
) : isSales ? ( <FinanceSidebar />
<SalesSidebar /> ) : isSales ? (
) : isManagement ? ( <SalesSidebar />
<ManagementSidebar /> ) : isManagement ? (
) : isDeveloper ? ( <ManagementSidebar />
<DeveloperSidebar /> ) : isDeveloper ? (
) : ( <DeveloperSidebar />
<ProductionSidebar /> // Default to production sidebar ) : (
)} <ProductionSidebar /> // Default to production sidebar
<Layout style={{ padding: '24px' }}> )}
<Content> <Layout style={{ padding: '24px' }}>
<Flex vertical style={{ height: '100%' }} gap='20px'> <Content>
<Flex justify='space-between'> <Flex vertical style={{ height: '100%' }} gap='20px'>
<DashboardBreadcrumb style={{ margin: '16px 0' }} /> <Flex justify='space-between'>
</Flex> <DashboardBreadcrumb style={{ margin: '16px 0' }} />
</Flex>
{children} {children}
</Flex> </Flex>
</Content> </Content>
</Layout>
</Layout> </Layout>
</Layout> </Layout>
</Layout> </TableStateProvider>
</MessageProvider> </MessageProvider>
) )
} }

View File

@ -33,7 +33,6 @@ 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)
@ -183,7 +182,6 @@ 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
} }
@ -245,7 +243,6 @@ 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
} }
@ -275,8 +272,7 @@ 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}
/> />
@ -322,26 +318,11 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
/> />
</Space> </Space>
</Space> </Space>
<Flex gap='small'> <ObjectTableNavigationButtons
<PrinterControlButtons disabled={objectFormState.loading}
callAction={(action) => _id={printerId}
actionHandlerRef.current?.callAction(action) objectType='printer'
} />
objectData={objectFormState.objectData}
disabled={
!objectFormState.objectData?.online || objectFormState.loading
}
loading={objectFormState.loading}
/>
{!slicerIntegration && (
<ObjectTableNavigationButtons
showEndingDivider={true}
disabled={objectFormState.loading}
_id={printerId}
objectType='printer'
/>
)}
</Flex>
</Flex> </Flex>
<AlertsDisplay <AlertsDisplay

View File

@ -36,6 +36,8 @@ 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...'} />
} }

View File

@ -1,10 +1,6 @@
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',
@ -63,9 +59,7 @@ const KeyboardShortcut = ({
} }
if (log) { if (log) {
logger.info('Key Down:', key, 'Pressed Keys:', [ console.log('Key Down:', key, 'Pressed Keys:', [...pressedKeysRef.current])
...pressedKeysRef.current
])
} }
if ( if (

View File

@ -37,95 +37,54 @@ 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( function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
actions, return actions.map((action) => {
currentUrlWithActions, if (action.type === 'divider') {
id, return { type: 'divider' }
objectData, }
userProfile const actionUrl = action.url ? action.url(id) : undefined
) {
return cleanDividers(
actions.map((action) => {
if (action.type === 'divider') {
return { type: 'divider' }
}
const actionUrl = action.url ? action.url(id) : undefined
var disabled = actionUrl && actionUrl === currentUrlWithActions var disabled = actionUrl && actionUrl === currentUrlWithActions
var visible = true var visible = true
if (action.disabled) { const { userProfile } = useContext(AuthContext)
if (typeof action.disabled === 'function') {
disabled = action.disabled({ ...objectData, _user: userProfile })
} else {
disabled = action.disabled
}
}
if (action.visible) { if (action.disabled) {
if (typeof action.visible === 'function') { if (typeof action.disabled === 'function') {
visible = action.visible(objectData) disabled = action.disabled({ ...objectData, _user: userProfile })
} else { } else {
visible = action.visible disabled = action.disabled
}
} }
}
if (visible != true) { if (action.visible) {
return null if (typeof action.visible === 'function') {
visible = action.visible(objectData)
} else {
visible = action.visible
} }
}
const item = { const item = {
key: action.key || action.name, key: action.key || action.name,
label: action.label, label: action.label,
danger: action?.danger || false, danger: action?.danger || false,
icon: action.icon ? createElement(action.icon) : undefined, icon: action.icon ? createElement(action.icon) : undefined,
disabled disabled
} }
if (action.children && Array.isArray(action.children)) { if (action.children && Array.isArray(action.children)) {
item.children = mapActionsToMenuItems( item.children = mapActionsToMenuItems(
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) => {
@ -149,7 +108,6 @@ 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,
@ -162,13 +120,10 @@ const ObjectActions = ({
visibleActions visibleActions
) )
const filteredActions = cleanDividers( const filteredActions = visibilityFilteredActions.filter(
visibilityFilteredActions.filter( (action) =>
(action) => typeof action.url !== 'function' ||
action.type === 'divider' || action.url(id) !== currentUrlWithoutActions
typeof action.url !== 'function' ||
action.url(id) !== currentUrlWithoutActions
)
) )
const currentUrlWithActions = location.pathname + location.search const currentUrlWithActions = location.pathname + location.search
@ -179,8 +134,7 @@ 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

View File

@ -127,6 +127,8 @@ 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 })
}, },
@ -535,7 +537,7 @@ const ObjectForm = forwardRef(
} }
const activityUnsubscribe = subscribeToObjectActivity( const activityUnsubscribe = subscribeToObjectActivity(
id?.toLowerCase(), id,
type, type,
activityHandler activityHandler
) )
@ -563,7 +565,7 @@ const ObjectForm = forwardRef(
} }
const objectUpdatesUnsubscribe = subscribeToObjectUpdates( const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
id?.toLowerCase(), id,
type, type,
objectUpdateHandler objectUpdateHandler
) )

View File

@ -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?.toLowerCase(), itemId,
type, type,
(updateData) => { (updateData) => {
updateEventHandlerRef.current(itemId, updateData) updateEventHandlerRef.current(itemId, updateData)

View File

@ -1,89 +0,0 @@
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

View File

@ -7,12 +7,7 @@ import merge from 'lodash/merge'
const { Text } = Typography const { Text } = Typography
const PrinterMiscPanel = ({ const PrinterMiscPanel = ({ id, showControls = true, disabled = false }) => {
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
@ -40,22 +35,10 @@ const PrinterMiscPanel = ({
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?.toLowerCase(), id,
'printer', 'printer',
'misc', 'misc',
(event) => { (event) => {
@ -210,8 +193,7 @@ const PrinterMiscPanel = ({
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

View File

@ -30,23 +30,11 @@ 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)
@ -67,30 +55,10 @@ 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?.toLowerCase(), id,
'printer', 'printer',
'motion', 'motion',
(event) => { (event) => {
@ -105,7 +73,6 @@ 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)
@ -245,22 +212,16 @@ const PrinterPositionPanel = ({
</Descriptions> </Descriptions>
{showControls && ( {showControls && (
<> <>
<Flex vertical gap='small' style={{ width: '100%' }}> <Space direction='vertical' style={{ width: '100%' }}>
<Flex align='center' style={{ width: '100%' }}> <Space direction='horizontal'>
<Text style={{ ...controlLabelStyle, minWidth: '91px' }}> <Text>Max Velocity:</Text>
Max Velocity: <Space.Compact block size='small'>
</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={controlInputStyle} style={{ width: '125px' }}
suffix='mm/s' suffix='mm/s'
onChange={(value) => setMaxVelocity(value)} onChange={(value) => setMaxVelocity(value)}
onPressEnter={handleSetMaxVelocity} onPressEnter={handleSetMaxVelocity}
@ -276,104 +237,92 @@ const PrinterPositionPanel = ({
Set Set
</Button> </Button>
</Space.Compact> </Space.Compact>
</Flex> </Space>
<Flex align='center' style={{ width: '100%' }}> <Space direction='vertical' style={{ width: '100%' }}>
<Text style={{ ...controlLabelStyle, minWidth: '120px' }}> <Space direction='horizontal'>
Max Acceleration: <Text>Max Acceleration:</Text>
</Text> <Space.Compact block size='small'>
<Space.Compact <InputNumber
block value={round(maxAcceleration, 2)}
size='small' min={1}
style={{ flex: 1, minWidth: 0 }} max={10000}
> step={5}
<InputNumber style={{ width: '125px' }}
value={round(maxAcceleration, 2)} suffix='mm/s²'
min={1} onChange={(value) => setMaxAcceleration(value)}
max={10000} onPressEnter={handleSetMaxAcceleration}
step={5} size='small'
style={controlInputStyle} disabled={controlsDisabled}
suffix='mm/s²' />
onChange={(value) => setMaxAcceleration(value)} <Button
onPressEnter={handleSetMaxAcceleration} type='default'
size='small' style={{ width: 40 }}
disabled={controlsDisabled} onClick={handleSetMaxAcceleration}
/> disabled={controlsDisabled}
<Button >
type='default' Set
style={{ width: 40 }} </Button>
onClick={handleSetMaxAcceleration} </Space.Compact>
disabled={controlsDisabled} </Space>
> </Space>
Set
</Button>
</Space.Compact>
</Flex>
<Flex align='center' style={{ width: '100%' }}> <Space direction='vertical' style={{ width: '100%' }}>
<Text style={{ ...controlLabelStyle, minWidth: '102px' }}> <Space direction='horizontal'>
Sqr Corner Vel: <Text>Sqr Corner Vel:</Text>
</Text> <Space.Compact block size='small'>
<Space.Compact <InputNumber
block value={round(squareCornerVelocity, 2) || 0}
size='small' min={0.1}
style={{ flex: 1, minWidth: 0 }} max={1000}
> step={0.1}
<InputNumber suffix='mm/s'
value={round(squareCornerVelocity, 2) || 0} style={{ width: '125px' }}
min={0.1} onChange={(value) => setSquareCornerVelocity(value)}
max={1000} onPressEnter={handleSetSquareCornerVelocity}
step={0.1} size='small'
style={controlInputStyle} disabled={controlsDisabled}
suffix='mm/s' />
onChange={(value) => setSquareCornerVelocity(value)} <Button
onPressEnter={handleSetSquareCornerVelocity} type='default'
size='small' style={{ width: 40 }}
disabled={controlsDisabled} onClick={handleSetSquareCornerVelocity}
/> disabled={controlsDisabled}
<Button >
type='default' Set
style={{ width: 40 }} </Button>
onClick={handleSetSquareCornerVelocity} </Space.Compact>
disabled={controlsDisabled} </Space>
> </Space>
Set
</Button>
</Space.Compact>
</Flex>
<Flex align='center' style={{ width: '100%' }}> <Space direction='vertical' style={{ width: '100%' }}>
<Text style={{ ...controlLabelStyle, minWidth: '114px' }}> <Space direction='horizontal'>
Min Cruise Ratio: <Text>Min Cruise Ratio:</Text>
</Text> <Space.Compact block size='small'>
<Space.Compact <InputNumber
block value={round(minCruiseRatio * 100, 2) || 0}
size='small' min={0}
style={{ flex: 1, minWidth: 0 }} max={100}
> step={1}
<InputNumber suffix='%'
value={round(minCruiseRatio * 100, 2) || 0} style={{ width: '125px' }}
min={0} onChange={(value) => setMinCruiseRatio(value / 100)}
max={100} onPressEnter={handleSetMinCruiseRatio}
step={1} size='small'
suffix='%' disabled={controlsDisabled}
style={controlInputStyle} />
onChange={(value) => setMinCruiseRatio(value / 100)} <Button
onPressEnter={handleSetMinCruiseRatio} type='default'
size='small' style={{ width: 40 }}
disabled={controlsDisabled} onClick={handleSetMinCruiseRatio}
/> disabled={controlsDisabled}
<Button >
type='default' Set
style={{ width: 40 }} </Button>
onClick={handleSetMinCruiseRatio} </Space.Compact>
disabled={controlsDisabled} </Space>
> </Space>
Set </Space>
</Button>
</Space.Compact>
</Flex>
</Flex>
</> </>
)} )}
<Text>Homing Origin:</Text> <Text>Homing Origin:</Text>
@ -449,22 +398,16 @@ const PrinterPositionPanel = ({
</Descriptions> </Descriptions>
{showControls && ( {showControls && (
<> <>
<Flex vertical gap='small' style={{ width: '100%' }}> <Space direction='vertical' style={{ width: '100%' }}>
<Flex align='center' style={{ width: '100%' }}> <Space direction='horizontal'>
<Text style={{ ...controlLabelStyle, minWidth: '96px' }}> <Text>Speed Factor:</Text>
Speed Factor: <Space.Compact block size='small'>
</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={controlInputStyle} style={{ width: '125px' }}
suffix='%' suffix='%'
onChange={(value) => setSpeedFactor(value)} onChange={(value) => setSpeedFactor(value)}
onPressEnter={handleSetSpeedFactor} onPressEnter={handleSetSpeedFactor}
@ -480,23 +423,17 @@ const PrinterPositionPanel = ({
Set Set
</Button> </Button>
</Space.Compact> </Space.Compact>
</Flex> </Space>
<Flex align='center' style={{ width: '100%' }}> <Space direction='horizontal'>
<Text style={{ ...controlLabelStyle, minWidth: '105px' }}> <Text>Extrude Factor:</Text>
Extrude Factor: <Space.Compact block size='small'>
</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={controlInputStyle} style={{ width: '125px' }}
suffix='%' suffix='%'
onChange={(value) => setExtrudeFactor(value)} onChange={(value) => setExtrudeFactor(value)}
onPressEnter={handleSetExtrudeFactor} onPressEnter={handleSetExtrudeFactor}
@ -512,8 +449,8 @@ const PrinterPositionPanel = ({
Set Set
</Button> </Button>
</Space.Compact> </Space.Compact>
</Flex> </Space>
</Flex> </Space>
</> </>
)} )}
<Descriptions <Descriptions
@ -572,8 +509,7 @@ 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

View File

@ -38,8 +38,7 @@ 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: {
@ -56,17 +55,6 @@ 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
@ -84,9 +72,9 @@ const PrinterTemperaturePanel = ({
}, [temperatureData.bed?.target]) }, [temperatureData.bed?.target])
useEffect(() => { useEffect(() => {
if (id && connected == true && offline == false) { if (id && connected == true) {
const temperatureEventUnsubscribe = subscribeToObjectEvent( const temperatureEventUnsubscribe = subscribeToObjectEvent(
id?.toLowerCase(), id,
'printer', 'printer',
'temperature', 'temperature',
(event) => { (event) => {
@ -100,7 +88,7 @@ const PrinterTemperaturePanel = ({
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe() if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
} }
} }
}, [id, connected, subscribeToObjectEvent, offline]) }, [id, connected, subscribeToObjectEvent])
const [extruderTarget, setExtruderTarget] = useState(0) const [extruderTarget, setExtruderTarget] = useState(0)
const [bedTarget, setBedTarget] = useState(0) const [bedTarget, setBedTarget] = useState(0)
@ -189,7 +177,7 @@ const PrinterTemperaturePanel = ({
value={extruderTarget} value={extruderTarget}
min={0} min={0}
max={300} max={300}
style={{ width: '140px' }} style={{ width: '120px' }}
addonAfter='°C' addonAfter='°C'
onChange={(value) => setExtruderTarget(value || 0)} onChange={(value) => setExtruderTarget(value || 0)}
onPressEnter={() => onPressEnter={() =>
@ -253,7 +241,7 @@ const PrinterTemperaturePanel = ({
value={bedTarget} value={bedTarget}
min={0} min={0}
max={300} max={300}
style={{ width: '140px' }} style={{ width: '120px' }}
addonAfter='°C' addonAfter='°C'
onChange={(value) => setBedTarget(value || 0)} onChange={(value) => setBedTarget(value || 0)}
onPressEnter={() => onPressEnter={() =>
@ -321,8 +309,7 @@ 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

View File

@ -42,6 +42,7 @@ const Thumbnail = function Thumbnail({
borderRadius: '5px', borderRadius: '5px',
width: size, width: size,
height: size, height: size,
border: 'none',
...style ...style
} }
@ -150,7 +151,7 @@ const Thumbnail = function Thumbnail({
<Card <Card
className={className} className={className}
style={cardStyle} style={cardStyle}
styles={{ body: { padding: 0, height: '100%', width: style?.width } }} styles={{ body: { padding: 0, height: '100%' } }}
> >
<Flex justify='center' align='center' style={{ height: '100%' }}> <Flex justify='center' align='center' style={{ height: '100%' }}>
{fallback || ( {fallback || (

View File

@ -1510,7 +1510,7 @@ const ApiServerProvider = ({ children }) => {
} }
} }
const fetchFileThumbnail = async (file, size, show404Error = false) => { const fetchFileThumbnail = async (file, size) => {
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,9 +1528,6 @@ 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)

View File

@ -164,7 +164,6 @@ export const GCodeFile = {
showPreview: false, showPreview: false,
showHyperlink: true, showHyperlink: true,
masterFilter: ['.gcode', '.g'], masterFilter: ['.gcode', '.g'],
thumbnail: true,
columnWidth: 200 columnWidth: 200
}, },
{ {