Tom Butcher dcbf308988
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Refactor dashboard components to replace LockIndicator with ActivityIndicator
- Updated various components to use ActivityIndicator instead of LockIndicator for better activity tracking.
- Adjusted layout spacing by changing Space component size from 'middle' to 'small' for improved UI consistency.
- Modified object form state to include activities instead of lock status, enhancing the user experience during editing.
2026-07-26 00:51:36 +01:00

533 lines
20 KiB
JavaScript

import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Card, Flex, Space } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import ActionHandler from '../../common/ActionHandler'
import AuditLogIcon from '../../../Icons/AuditLogIcon'
import DocumentPrintButton from '../../common/DocumentPrintButton'
import EditButtons from '../../common/EditButtons'
import FilamentIcon from '../../../Icons/FilamentIcon'
import GCodeFileIcon from '../../../Icons/GCodeFileIcon'
import HotEndIcon from '../../../Icons/HotEndIcon'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon'
import InfoCollapse from '../../common/InfoCollapse'
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
import ActivityIndicator from '../../common/ActivityIndicator'
import MultiMotionIcon from '../../../Icons/MultiMotionIcon'
import NoteIcon from '../../../Icons/NoteIcon'
import NotesPanel from '../../common/NotesPanel'
import ObjectActions from '../../common/ObjectActions'
import ObjectForm from '../../common/ObjectForm'
import ObjectInfo from '../../common/ObjectInfo'
import ObjectTable from '../../common/ObjectTable'
import RulerIcon from '../../../Icons/RulerIcon'
import ScrollBox from '../../common/ScrollBox'
import SettingsIcon from '../../../Icons/SettingsIcon'
import UserNotifierToggle from '../../common/UserNotifierToggle'
import ViewButton from '../../common/ViewButton'
import useCollapseState from '../../hooks/useCollapseState'
import {
FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES,
FILAMENT_PROFILE_COOLING_PROPERTIES,
FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES,
FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
FILAMENT_PROFILE_GCODE_PROPERTIES,
FILAMENT_PROFILE_LOADING_PROPERTIES,
FILAMENT_PROFILE_MATERIAL_PROPERTIES,
FILAMENT_PROFILE_RETRACTION_PROPERTIES,
FILAMENT_PROFILE_SHRINKAGE_DRYING_PROPERTIES,
FILAMENT_PROFILE_SPEED_PROPERTIES,
FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
FILAMENT_PROFILE_WIPE_TOWER_PROPERTIES
} from '../../../../database/models/FilamentProfile'
const toVisibleProperties = (propertyNames) =>
Object.fromEntries(propertyNames.map((name) => [name, true]))
const FilamentProfileInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const filamentProfileId = new URLSearchParams(location.search).get(
'filamentProfileId'
)
const [collapseState, updateCollapseState] = useCollapseState(
'FilamentProfileInfo',
{
info: true,
material: true,
flowPressure: true,
temperature: true,
bedTemperature: true,
speed: true,
cooling: true,
exhaustFan: true,
retraction: true,
loading: true,
wipeTower: true,
gCodeSettings: true,
compatibility: true,
shrinkageDrying: true,
notes: true,
auditLogs: false
}
)
const [objectFormState, setObjectFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false,
objectData: {}
})
const actions = {
edit: () => {
objectFormRef.current?.startEditing?.()
return false
},
cancelEdit: () => {
objectFormRef.current?.cancelEditing?.()
return true
},
finishEdit: () => {
objectFormRef.current?.handleUpdate?.()
return true
},
delete: () => {
objectFormRef.current?.handleDelete?.()
return true
}
}
return (
<Flex
gap='large'
vertical='true'
style={{ maxHeight: '100%', minHeight: 0 }}
>
<Flex justify='space-between'>
<Space size='small'>
<Space size='small'>
<ObjectActions
type='filamentProfile'
id={filamentProfileId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Filament Profile Information' },
{ key: 'material', label: 'Material' },
{ key: 'flowPressure', label: 'Flow & Pressure' },
{ key: 'temperature', label: 'Temperature' },
{ key: 'bedTemperature', label: 'Bed Temperatures' },
{ key: 'speed', label: 'Speed' },
{ key: 'cooling', label: 'Part Cooling Fan' },
{ key: 'exhaustFan', label: 'Exhaust Fan' },
{ key: 'retraction', label: 'Retraction Overrides' },
{ key: 'loading', label: 'Loading & Multi-tool' },
{ key: 'wipeTower', label: 'Wipe Tower & Flushing' },
{ key: 'gCodeSettings', label: 'G-Code Settings' },
{ key: 'compatibility', label: 'Compatibility' },
{ key: 'shrinkageDrying', label: 'Shrinkage & Drying' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='filamentProfile'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='filamentProfile'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
</Space>
<ActivityIndicator
activities={objectFormState.activities}
showStartingDivider={true}
/>
</Space>
<EditButtons
isEditing={objectFormState.isEditing}
handleUpdate={() => actionHandlerRef.current.callAction('finishEdit')}
cancelEditing={() =>
actionHandlerRef.current.callAction('cancelEdit')
}
startEditing={() => actionHandlerRef.current.callAction('edit')}
editLoading={objectFormState.editLoading}
formValid={objectFormState.formValid}
disabled={objectFormState.beingEditedByOther || objectFormState.loading}
loading={objectFormState.editLoading}
/>
</Flex>
<ScrollBox>
<Flex vertical gap='large'>
<ActionHandler
actions={actions}
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<ObjectForm
id={filamentProfileId}
type='filamentProfile'
ref={objectFormRef}
onStateChange={(state) =>
setObjectFormState((current) => ({ ...current, ...state }))
}
>
{({ loading, isEditing, objectData }) => (
<Flex vertical gap='large'>
<InfoCollapse
title='Filament Profile Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='140px'
visibleProperties={{
_id: true,
name: true,
_reference: true,
filamentType: true,
filament: true,
createdAt: true,
updatedAt: true
}}
/>
</InfoCollapse>
<InfoCollapse
title='Material'
icon={<FilamentIcon />}
active={collapseState.material}
onToggle={(expanded) =>
updateCollapseState('material', expanded)
}
collapseKey='material'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='220px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_MATERIAL_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Flow & Pressure'
icon={<SettingsIcon />}
active={collapseState.flowPressure}
onToggle={(expanded) =>
updateCollapseState('flowPressure', expanded)
}
collapseKey='flowPressure'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='325px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Temperature'
icon={<HotEndIcon />}
active={collapseState.temperature}
onToggle={(expanded) =>
updateCollapseState('temperature', expanded)
}
collapseKey='temperature'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='280px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_TEMPERATURE_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Bed Temperatures'
icon={<RulerIcon />}
active={collapseState.bedTemperature}
onToggle={(expanded) =>
updateCollapseState('bedTemperature', expanded)
}
collapseKey='bedTemperature'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='280px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Speed'
icon={<MultiMotionIcon />}
active={collapseState.speed}
onToggle={(expanded) =>
updateCollapseState('speed', expanded)
}
collapseKey='speed'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='265px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_SPEED_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Part Cooling Fan'
icon={<SettingsIcon />}
active={collapseState.cooling}
onToggle={(expanded) =>
updateCollapseState('cooling', expanded)
}
collapseKey='cooling'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='350px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_COOLING_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Exhaust Fan'
icon={<SettingsIcon />}
active={collapseState.exhaustFan}
onToggle={(expanded) =>
updateCollapseState('exhaustFan', expanded)
}
collapseKey='exhaustFan'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='285px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Retraction Overrides'
icon={<SettingsIcon />}
active={collapseState.retraction}
onToggle={(expanded) =>
updateCollapseState('retraction', expanded)
}
collapseKey='retraction'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='250px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_RETRACTION_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Loading & Multi-tool'
icon={<FilamentIcon />}
active={collapseState.loading}
onToggle={(expanded) =>
updateCollapseState('loading', expanded)
}
collapseKey='loading'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='220px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_LOADING_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Wipe Tower & Flushing'
icon={<SettingsIcon />}
active={collapseState.wipeTower}
onToggle={(expanded) =>
updateCollapseState('wipeTower', expanded)
}
collapseKey='wipeTower'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='330px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_WIPE_TOWER_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='G-Code Settings'
icon={<GCodeFileIcon />}
active={collapseState.gCodeSettings}
onToggle={(expanded) =>
updateCollapseState('gCodeSettings', expanded)
}
collapseKey='gCodeSettings'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='270px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_GCODE_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Compatibility'
icon={<SettingsIcon />}
active={collapseState.compatibility}
onToggle={(expanded) =>
updateCollapseState('compatibility', expanded)
}
collapseKey='compatibility'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='270px'
visibleProperties={toVisibleProperties(
FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES
)}
/>
</InfoCollapse>
<InfoCollapse
title='Shrinkage & Drying'
icon={<SettingsIcon />}
active={collapseState.shrinkageDrying}
onToggle={(expanded) =>
updateCollapseState('shrinkageDrying', expanded)
}
collapseKey='shrinkageDrying'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='filamentProfile'
objectData={objectData}
labelWidth='340px'
visibleProperties={{
...toVisibleProperties(
FILAMENT_PROFILE_SHRINKAGE_DRYING_PROPERTIES
),
filamentNotes: true
}}
/>
</InfoCollapse>
</Flex>
)}
</ObjectForm>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={filamentProfileId} type='filamentProfile' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) => updateCollapseState('auditLogs', expanded)}
collapseKey='auditLogs'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': filamentProfileId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
)
}
export default FilamentProfileInfo