Compare commits
No commits in common. "8bbfe20ec494b7e265fb8b41c56dd3785cc00f10" and "eba1a876644d9b8b856c183858b642897aad2f28" have entirely different histories.
8bbfe20ec4
...
eba1a87664
25
package.json
25
package.json
@ -30,14 +30,14 @@
|
||||
"@tsparticles/react": "^3.0.0",
|
||||
"@tsparticles/slim": "^3.9.1",
|
||||
"@uiw/react-codemirror": "^4.25.1",
|
||||
"antd": "^5.27.1",
|
||||
"antd": "^5.27.0",
|
||||
"antd-style": "^3.7.1",
|
||||
"axios": "^1.11.0",
|
||||
"country-list": "^2.4.1",
|
||||
"country-list": "^2.3.0",
|
||||
"cross-env": "^10.0.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^17.2.1",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
@ -50,12 +50,13 @@
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^19.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-country-flag": "^3.1.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-responsive": "^10.0.1",
|
||||
"react-router-dom": "^7.8.2",
|
||||
"react-router-dom": "^7.8.0",
|
||||
"react-stl-viewer": "^2.5.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"socket.io-client": "*",
|
||||
"standard": "^17.1.2",
|
||||
@ -95,12 +96,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@vitejs/plugin-react": "^5.0.2",
|
||||
"concurrently": "^9.2.1",
|
||||
"electron": "^37.4.0",
|
||||
"@vitejs/plugin-react": "^5.0.1",
|
||||
"concurrently": "^9.2.0",
|
||||
"electron": "^37.2.6",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-packager": "^17.1.2",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
@ -115,7 +116,7 @@
|
||||
"vite": "^7.1.3",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-svgo": "^2.0.0",
|
||||
"vite-plugin-svgr": "^4.5.0"
|
||||
"vite-plugin-svgr": "^4.3.0"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.tombutcher.farmcontrol",
|
||||
|
||||
@ -20,7 +20,7 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
||||
|
||||
const FilamentStockInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const filamentStockId = new URLSearchParams(location.search).get(
|
||||
'filamentStockId'
|
||||
@ -34,7 +34,7 @@ const FilamentStockInfo = () => {
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -44,19 +44,19 @@ const FilamentStockInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -74,10 +74,10 @@ const FilamentStockInfo = () => {
|
||||
<ObjectActions
|
||||
type='filamentStock'
|
||||
id={filamentStockId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Filament Stock Information' },
|
||||
{ key: 'events', label: 'Filament Stock Events' },
|
||||
@ -88,11 +88,11 @@ const FilamentStockInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -102,10 +102,10 @@ const FilamentStockInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -113,7 +113,7 @@ const FilamentStockInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -127,7 +127,7 @@ const FilamentStockInfo = () => {
|
||||
id={filamentStockId}
|
||||
type='filamentStock'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -150,7 +150,7 @@ const FilamentStockInfo = () => {
|
||||
onToggle={(expanded) => updateCollapseState('events', expanded)}
|
||||
collapseKey='events'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
@ -180,7 +180,7 @@ const FilamentStockInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -25,7 +25,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const DocumentSizeInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const documentSizeId = new URLSearchParams(location.search).get(
|
||||
'documentSizeId'
|
||||
@ -38,7 +38,7 @@ const DocumentSizeInfo = () => {
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -48,19 +48,19 @@ const DocumentSizeInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -78,10 +78,10 @@ const DocumentSizeInfo = () => {
|
||||
<ObjectActions
|
||||
type='documentSize'
|
||||
id={documentSizeId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Document Size Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -91,11 +91,11 @@ const DocumentSizeInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -105,10 +105,10 @@ const DocumentSizeInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -116,7 +116,7 @@ const DocumentSizeInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -130,7 +130,7 @@ const DocumentSizeInfo = () => {
|
||||
id={documentSizeId}
|
||||
type='documentSize'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -167,7 +167,7 @@ const DocumentSizeInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -21,7 +21,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const DocumentTemplateDesign = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const documentTemplateId = new URLSearchParams(location.search).get(
|
||||
'documentTemplateId'
|
||||
@ -36,7 +36,7 @@ const DocumentTemplateDesign = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -46,19 +46,19 @@ const DocumentTemplateDesign = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -78,11 +78,11 @@ const DocumentTemplateDesign = () => {
|
||||
<ObjectActions
|
||||
type='documentTemplate'
|
||||
id={documentTemplateId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
visibleActions={{ edit: false }}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{
|
||||
key: 'preview',
|
||||
@ -99,11 +99,11 @@ const DocumentTemplateDesign = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -113,10 +113,10 @@ const DocumentTemplateDesign = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -125,14 +125,14 @@ const DocumentTemplateDesign = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={documentTemplateId}
|
||||
type='documentTemplate'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
console.log('Got edit form state change', state)
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
|
||||
@ -25,7 +25,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const DocumentTemplateInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const documentTemplateId = new URLSearchParams(location.search).get(
|
||||
'documentTemplateId'
|
||||
@ -40,7 +40,7 @@ const DocumentTemplateInfo = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -50,19 +50,19 @@ const DocumentTemplateInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -83,10 +83,10 @@ const DocumentTemplateInfo = () => {
|
||||
<ObjectActions
|
||||
type='documentTemplate'
|
||||
id={documentTemplateId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'DocumentTemplate Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -96,11 +96,11 @@ const DocumentTemplateInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -110,10 +110,10 @@ const DocumentTemplateInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -122,7 +122,7 @@ const DocumentTemplateInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -136,7 +136,7 @@ const DocumentTemplateInfo = () => {
|
||||
id={documentTemplateId}
|
||||
type='documentTemplate'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
console.log('Got edit form state change', state)
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
@ -182,7 +182,7 @@ const DocumentTemplateInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -26,7 +26,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const FilamentInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const filamentId = new URLSearchParams(location.search).get('filamentId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
@ -39,7 +39,7 @@ const FilamentInfo = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -49,19 +49,19 @@ const FilamentInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -82,10 +82,10 @@ const FilamentInfo = () => {
|
||||
<ObjectActions
|
||||
type='filament'
|
||||
id={filamentId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Filament Information' },
|
||||
{ key: 'stocks', label: 'Filament Stocks' },
|
||||
@ -96,11 +96,11 @@ const FilamentInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -110,10 +110,10 @@ const FilamentInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -122,7 +122,7 @@ const FilamentInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -136,7 +136,7 @@ const FilamentInfo = () => {
|
||||
id={filamentId}
|
||||
type='filament'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -163,7 +163,7 @@ const FilamentInfo = () => {
|
||||
onToggle={(expanded) => updateCollapseState('stocks', expanded)}
|
||||
collapseKey='stocks'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
@ -199,7 +199,7 @@ const FilamentInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import NewObjectButtons from '../../common/NewObjectButtons'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const NewFilament = ({ onOk }) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
|
||||
return (
|
||||
<NewObjectForm type={'filament'}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -56,16 +66,43 @@ const NewFilament = ({ onOk }) => {
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Filament'
|
||||
<Flex gap='middle'>
|
||||
{!isMobile && (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||
)}
|
||||
|
||||
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
New Filament
|
||||
</Title>
|
||||
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||
{steps[currentStep].content}
|
||||
</div>
|
||||
<NewObjectButtons
|
||||
currentStep={currentStep}
|
||||
totalSteps={steps.length}
|
||||
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
formValid={formValid}
|
||||
submitLoading={submitLoading}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
|
||||
@ -26,7 +26,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const HostInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const hostId = new URLSearchParams(location.search).get('hostId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('HostInfo', {
|
||||
@ -37,7 +37,7 @@ const HostInfo = () => {
|
||||
})
|
||||
|
||||
const [hostOTPOpen, setHostOTPOpen] = useState(false)
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -47,7 +47,7 @@ const HostInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
hostOTP: () => {
|
||||
@ -55,15 +55,15 @@ const HostInfo = () => {
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -84,10 +84,10 @@ const HostInfo = () => {
|
||||
<ObjectActions
|
||||
type='host'
|
||||
id={hostId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Host Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -97,11 +97,11 @@ const HostInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -111,10 +111,10 @@ const HostInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -123,7 +123,7 @@ const HostInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -137,7 +137,7 @@ const HostInfo = () => {
|
||||
id={hostId}
|
||||
type='host'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
console.log('Got edit form state change', state)
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
@ -179,7 +179,7 @@ const HostInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -1,80 +1,279 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
message,
|
||||
Select,
|
||||
Flex,
|
||||
Steps,
|
||||
Divider,
|
||||
Upload,
|
||||
Descriptions
|
||||
} from 'antd'
|
||||
import { UploadOutlined } from '@ant-design/icons'
|
||||
import VendorSelect from '../../common/VendorSelect'
|
||||
|
||||
const NewMaterial = ({ onOk }) => {
|
||||
return (
|
||||
<NewObjectForm type={'material'}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
import config from '../../../../config'
|
||||
|
||||
const initialNewMaterialForm = {
|
||||
name: '',
|
||||
vendor: { id: null, name: '' },
|
||||
category: '',
|
||||
image: null,
|
||||
url: '',
|
||||
barcode: ''
|
||||
}
|
||||
|
||||
const NewMaterial = ({ onSuccess }) => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
|
||||
const [newMaterialLoading, setNewMaterialLoading] = useState(false)
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [nextEnabled, setNextEnabled] = useState(false)
|
||||
|
||||
const [newMaterialForm] = Form.useForm()
|
||||
const [newMaterialFormValues, setNewMaterialFormValues] = useState(
|
||||
initialNewMaterialForm
|
||||
)
|
||||
|
||||
const [imageList, setImageList] = useState([])
|
||||
|
||||
const newMaterialFormUpdateValues = Form.useWatch([], newMaterialForm)
|
||||
|
||||
useEffect(() => {
|
||||
newMaterialForm
|
||||
.validateFields({
|
||||
validateOnly: true
|
||||
})
|
||||
.then(() => setNextEnabled(true))
|
||||
.catch(() => setNextEnabled(false))
|
||||
}, [newMaterialForm, newMaterialFormUpdateValues])
|
||||
|
||||
const summaryItems = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='material'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
key: 'name',
|
||||
label: 'Name',
|
||||
children: newMaterialFormValues.name
|
||||
},
|
||||
{
|
||||
key: 'vendor',
|
||||
label: 'Vendor',
|
||||
children: newMaterialFormValues.vendor.name
|
||||
},
|
||||
{
|
||||
key: 'category',
|
||||
label: 'Category',
|
||||
children: newMaterialFormValues.category
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: 'Image',
|
||||
children: newMaterialFormValues.image && (
|
||||
<img
|
||||
src={newMaterialFormValues.image}
|
||||
style={{ width: 128 }}
|
||||
alt='Material'
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
key: 'url',
|
||||
label: 'URL',
|
||||
children: newMaterialFormValues.url
|
||||
},
|
||||
{
|
||||
key: 'barcode',
|
||||
label: 'Barcode',
|
||||
children: newMaterialFormValues.barcode
|
||||
}
|
||||
]
|
||||
|
||||
const handleNewMaterial = async () => {
|
||||
setNewMaterialLoading(true)
|
||||
try {
|
||||
await axios.post(
|
||||
`${config.backendUrl}/materials`,
|
||||
newMaterialFormValues,
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
)
|
||||
messageApi.success('New material created successfully.')
|
||||
onSuccess()
|
||||
} catch (error) {
|
||||
messageApi.error('Error creating new material: ' + error.message)
|
||||
} finally {
|
||||
setNewMaterialLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const getBase64 = (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => resolve(reader.result)
|
||||
reader.onerror = (error) => reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
const handleImageUpload = async ({ file, fileList }) => {
|
||||
if (fileList.length === 0) {
|
||||
setImageList(fileList)
|
||||
newMaterialForm.setFieldsValue({ image: '' })
|
||||
return
|
||||
}
|
||||
const base64 = await getBase64(file)
|
||||
setNewMaterialFormValues((prevValues) => ({
|
||||
...prevValues,
|
||||
image: base64
|
||||
}))
|
||||
fileList[0].name = 'Material Image'
|
||||
setImageList(fileList)
|
||||
newMaterialForm.setFieldsValue({ image: base64 })
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: 'Details',
|
||||
key: 'details',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='material'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
<>
|
||||
<Form.Item
|
||||
label='Name'
|
||||
name='name'
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a name.'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label='Vendor'
|
||||
name='vendor'
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select a vendor.'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<VendorSelect />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label='Category'
|
||||
name='category'
|
||||
rules={[{ required: true, message: 'Please select a category' }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value='Raw Material'>Raw Material</Select.Option>
|
||||
<Select.Option value='Component'>Component</Select.Option>
|
||||
<Select.Option value='Consumable'>Consumable</Select.Option>
|
||||
<Select.Option value='Tool'>Tool</Select.Option>
|
||||
<Select.Option value='Packaging'>Packaging</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Additional Info',
|
||||
key: 'additional',
|
||||
content: (
|
||||
<>
|
||||
<Form.Item label='Image' name='image'>
|
||||
<Upload
|
||||
listType='picture'
|
||||
maxCount={1}
|
||||
fileList={imageList}
|
||||
onChange={handleImageUpload}
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>Upload Image</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item label='URL' name='url'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label='Barcode' name='barcode'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='material'
|
||||
<>
|
||||
<Descriptions
|
||||
title='Material Details'
|
||||
bordered
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
items={summaryItems}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Material'
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
<>
|
||||
{contextHolder}
|
||||
<Form
|
||||
form={newMaterialForm}
|
||||
layout='vertical'
|
||||
onValuesChange={(changedValues) => {
|
||||
setNewMaterialFormValues((prevValues) => ({
|
||||
...prevValues,
|
||||
...changedValues
|
||||
}))
|
||||
}}
|
||||
>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps.map((item) => ({ title: item.title }))}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
<div style={{ minHeight: 200 }}>{steps[currentStep].content}</div>
|
||||
<Divider />
|
||||
<Flex justify='space-between'>
|
||||
<Button
|
||||
disabled={currentStep === 0}
|
||||
onClick={() => setCurrentStep((prev) => prev - 1)}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
{currentStep < steps.length - 1 ? (
|
||||
<Button
|
||||
type='primary'
|
||||
disabled={!nextEnabled}
|
||||
onClick={() => setCurrentStep((prev) => prev + 1)}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type='primary'
|
||||
loading={newMaterialLoading}
|
||||
onClick={handleNewMaterial}
|
||||
>
|
||||
Create Material
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
NewMaterial.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
onSuccess: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default NewMaterial
|
||||
|
||||
@ -1,9 +1,18 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import NewObjectButtons from '../../common/NewObjectButtons'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const NewNoteType = ({ onOk }) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'noteType'}
|
||||
@ -61,16 +70,43 @@ const NewNoteType = ({ onOk }) => {
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Note Type'
|
||||
<Flex gap='middle'>
|
||||
{!isMobile && (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||
)}
|
||||
|
||||
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
New Note Type
|
||||
</Title>
|
||||
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||
{steps[currentStep].content}
|
||||
</div>
|
||||
<NewObjectButtons
|
||||
currentStep={currentStep}
|
||||
totalSteps={steps.length}
|
||||
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
formValid={formValid}
|
||||
submitLoading={submitLoading}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
|
||||
@ -18,7 +18,7 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
|
||||
const NoteTypeInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const noteTypeId = new URLSearchParams(location.search).get('noteTypeId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
@ -28,7 +28,7 @@ const NoteTypeInfo = () => {
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -38,19 +38,19 @@ const NoteTypeInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -68,10 +68,10 @@ const NoteTypeInfo = () => {
|
||||
<ObjectActions
|
||||
type='noteType'
|
||||
id={noteTypeId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Note Type Information' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
@ -80,11 +80,11 @@ const NoteTypeInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -94,10 +94,10 @@ const NoteTypeInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -105,7 +105,7 @@ const NoteTypeInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -119,7 +119,7 @@ const NoteTypeInfo = () => {
|
||||
id={noteTypeId}
|
||||
type='noteType'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -145,7 +145,7 @@ const NoteTypeInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewNote = ({ onOk, defaultValues = {} }) => {
|
||||
return (
|
||||
<NewObjectForm type={'note'} defaultValues={defaultValues}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='note'
|
||||
column={1}
|
||||
visibleProperties={{ 'parent._id': false }}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
showSteps={false}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Note'
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewNote.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewNote
|
||||
@ -1,184 +0,0 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config'
|
||||
import useCollapseState from '../../hooks/useCollapseState'
|
||||
import NotesPanel from '../../common/NotesPanel'
|
||||
import InfoCollapse from '../../common/InfoCollapse'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import ViewButton from '../../common/ViewButton'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
|
||||
const log = loglevel.getLogger('NoteInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const NoteInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const noteId = new URLSearchParams(location.search).get('noteId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('NoteInfo', {
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
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='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='note'
|
||||
id={noteId}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Note Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</Space>
|
||||
<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.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
title='Note Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectForm
|
||||
id={noteId}
|
||||
type='note'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
type='note'
|
||||
objectData={objectData}
|
||||
/>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={noteId} type='note' />
|
||||
</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': noteId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</div>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoteInfo
|
||||
@ -20,7 +20,7 @@ import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
|
||||
const PartInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const partId = new URLSearchParams(location.search).get('partId')
|
||||
const { fetchObjectContent } = useContext(ApiServerContext)
|
||||
@ -30,7 +30,7 @@ const PartInfo = () => {
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -40,24 +40,24 @@ const PartInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
download: () => {
|
||||
if (partId && objectFormRef?.current?.getObjectData) {
|
||||
const objectData = objectFormRef.current.getObjectData()
|
||||
if (partId && editFormRef?.current?.getObjectData) {
|
||||
const objectData = editFormRef.current.getObjectData()
|
||||
fetchObjectContent(partId, 'part', `${objectData?.name || 'part'}.stl`)
|
||||
return true
|
||||
}
|
||||
@ -77,10 +77,10 @@ const PartInfo = () => {
|
||||
<ObjectActions
|
||||
type='part'
|
||||
id={partId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Part Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -90,11 +90,11 @@ const PartInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -104,10 +104,10 @@ const PartInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -115,7 +115,7 @@ const PartInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -129,7 +129,7 @@ const PartInfo = () => {
|
||||
id={partId}
|
||||
type='part'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -165,7 +165,7 @@ const PartInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
import { DeleteOutlined, EyeOutlined } from '@ant-design/icons'
|
||||
import { AuthContext } from '../../context/AuthContext'
|
||||
import PartIcon from '../../../Icons/PartIcon'
|
||||
import { StlViewer } from 'react-stl-viewer'
|
||||
import VendorSelect from '../../common/VendorSelect'
|
||||
|
||||
import config from '../../../../config'
|
||||
@ -481,7 +482,15 @@ const NewProduct = ({ onOk, reset }) => {
|
||||
>
|
||||
<Flex style={{ minWidth: '100%', minHeight: '80vh' }}>
|
||||
{previewFile && !isPreviewLoading ? (
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<StlViewer
|
||||
url={fileUrls[previewFile.uid]}
|
||||
orbitControls
|
||||
shadows
|
||||
style={{ height: '80vh', width: '100%' }}
|
||||
modelProps={{ color: '#008675' }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
|
||||
@ -20,7 +20,7 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
|
||||
const ProductInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const productId = new URLSearchParams(location.search).get('productId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('ProductInfo', {
|
||||
@ -29,7 +29,7 @@ const ProductInfo = () => {
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -39,19 +39,19 @@ const ProductInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -69,10 +69,10 @@ const ProductInfo = () => {
|
||||
<ObjectActions
|
||||
type='product'
|
||||
id={productId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Product Information' },
|
||||
{ key: 'parts', label: 'Product Parts' },
|
||||
@ -83,11 +83,11 @@ const ProductInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -97,10 +97,10 @@ const ProductInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -108,7 +108,7 @@ const ProductInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -122,7 +122,7 @@ const ProductInfo = () => {
|
||||
id={productId}
|
||||
type='product'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -174,7 +174,7 @@ const ProductInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -20,7 +20,7 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
|
||||
const UserInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const userId = new URLSearchParams(location.search).get('userId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
|
||||
@ -28,7 +28,7 @@ const UserInfo = () => {
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -38,19 +38,19 @@ const UserInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
editFormRef?.current?.fetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -68,10 +68,10 @@ const UserInfo = () => {
|
||||
<ObjectActions
|
||||
type='user'
|
||||
id={userId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'User Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -81,11 +81,11 @@ const UserInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -95,10 +95,10 @@ const UserInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -106,7 +106,7 @@ const UserInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -120,7 +120,7 @@ const UserInfo = () => {
|
||||
id={userId}
|
||||
type='user'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -157,7 +157,7 @@ const UserInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -1,9 +1,18 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import NewObjectButtons from '../../common/NewObjectButtons'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const NewVendor = ({ onOk }) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
|
||||
return (
|
||||
<NewObjectForm type={'vendor'}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -56,16 +65,43 @@ const NewVendor = ({ onOk }) => {
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Vendor'
|
||||
<Flex gap='middle'>
|
||||
{!isMobile && (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||
)}
|
||||
|
||||
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
New Vendor
|
||||
</Title>
|
||||
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||
{steps[currentStep].content}
|
||||
</div>
|
||||
<NewObjectButtons
|
||||
currentStep={currentStep}
|
||||
totalSteps={steps.length}
|
||||
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
formValid={formValid}
|
||||
submitLoading={submitLoading}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
|
||||
@ -24,7 +24,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const VendorInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const vendorId = new URLSearchParams(location.search).get('vendorId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('VendorInfo', {
|
||||
@ -32,7 +32,7 @@ const VendorInfo = () => {
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -42,23 +42,23 @@ const VendorInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
editFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
editFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
editFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
editFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
editFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -76,10 +76,10 @@ const VendorInfo = () => {
|
||||
<ObjectActions
|
||||
type='vendor'
|
||||
id={vendorId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Vendor Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -89,11 +89,11 @@ const VendorInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -103,10 +103,10 @@ const VendorInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -114,7 +114,7 @@ const VendorInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -128,7 +128,7 @@ const VendorInfo = () => {
|
||||
id={vendorId}
|
||||
type='vendor'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -164,7 +164,7 @@ const VendorInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -28,7 +28,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const GCodeFileInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const gcodeFileId = new URLSearchParams(location.search).get('gcodeFileId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
@ -41,7 +41,7 @@ const GCodeFileInfo = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -51,19 +51,19 @@ const GCodeFileInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -84,10 +84,10 @@ const GCodeFileInfo = () => {
|
||||
<ObjectActions
|
||||
type='gcodeFile'
|
||||
id={gcodeFileId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'GCode File Information' },
|
||||
{ key: 'preview', label: 'GCode File Preview' },
|
||||
@ -98,11 +98,11 @@ const GCodeFileInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -112,10 +112,10 @@ const GCodeFileInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -124,14 +124,14 @@ const GCodeFileInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={gcodeFileId}
|
||||
type='gcodeFile'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
console.log('Got edit form state change', state)
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
@ -206,7 +206,7 @@ const GCodeFileInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -26,7 +26,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const JobInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const jobId = new URLSearchParams(location.search).get('jobId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('JobInfo', {
|
||||
@ -36,7 +36,7 @@ const JobInfo = () => {
|
||||
auditLogs: true
|
||||
})
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -46,19 +46,19 @@ const JobInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -79,10 +79,10 @@ const JobInfo = () => {
|
||||
<ObjectActions
|
||||
type='job'
|
||||
id={jobId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Job Information' },
|
||||
{ key: 'subJobs', label: 'Sub Jobs' },
|
||||
@ -93,11 +93,11 @@ const JobInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -107,10 +107,10 @@ const JobInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -119,7 +119,7 @@ const JobInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -133,7 +133,7 @@ const JobInfo = () => {
|
||||
id={jobId}
|
||||
type='job'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
@ -186,7 +186,7 @@ const JobInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ log.setLevel(config.logLevel)
|
||||
|
||||
const PrinterInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const editFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const printerId = new URLSearchParams(location.search).get('printerId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', {
|
||||
@ -35,7 +35,7 @@ const PrinterInfo = () => {
|
||||
auditLogs: true
|
||||
})
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
const [editFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
@ -45,20 +45,20 @@ const PrinterInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current.handleFetchObject()
|
||||
editFormRef?.current.handleFetchObject()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
editFormRef?.current.startEditing()
|
||||
console.log('CALLING START EDITING')
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
editFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
editFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -79,10 +79,10 @@ const PrinterInfo = () => {
|
||||
<ObjectActions
|
||||
type='printer'
|
||||
id={printerId}
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
disabled={editFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Printer Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
@ -92,11 +92,11 @@ const PrinterInfo = () => {
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
<LockIndicator lock={editFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
isEditing={editFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
@ -106,10 +106,10 @@ const PrinterInfo = () => {
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
editLoading={editFormState.editLoading}
|
||||
formValid={editFormState.formValid}
|
||||
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||
loading={editFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
@ -118,7 +118,7 @@ const PrinterInfo = () => {
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
loading={editFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
@ -132,7 +132,7 @@ const PrinterInfo = () => {
|
||||
id={printerId}
|
||||
type='printer'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
ref={editFormRef}
|
||||
onStateChange={(state) => {
|
||||
console.log('Got edit form state change', state)
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
@ -174,7 +174,7 @@ const PrinterInfo = () => {
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
{editFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
|
||||
@ -12,8 +12,7 @@ const breadcrumbNameMap = {
|
||||
developer: 'Developer',
|
||||
overview: 'Overview',
|
||||
info: 'Info',
|
||||
design: 'Design',
|
||||
control: 'Control'
|
||||
design: 'Design'
|
||||
}
|
||||
|
||||
const mainSections = ['production', 'inventory', 'management', 'developer']
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
import { Card, Splitter } from 'antd'
|
||||
import PropTypes from 'prop-types'
|
||||
import CodeBlockEditor from './CodeBlockEditor'
|
||||
import MarkdownDisplay from './MarkdownDisplay'
|
||||
|
||||
const MarkdownInput = ({ value, onChange }) => {
|
||||
return (
|
||||
<Splitter className={'farmcontrol-splitter'} style={{ height: '100%' }}>
|
||||
<Splitter.Panel>
|
||||
<Card>
|
||||
<CodeBlockEditor
|
||||
code={value}
|
||||
onChange={onChange}
|
||||
language='markdown'
|
||||
/>
|
||||
</Card>
|
||||
</Splitter.Panel>
|
||||
<Splitter.Panel>
|
||||
<Card style={{ height: '100%' }}>
|
||||
<MarkdownDisplay content={value} />
|
||||
</Card>
|
||||
</Splitter.Panel>
|
||||
</Splitter>
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownInput.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func
|
||||
}
|
||||
|
||||
export default MarkdownInput
|
||||
@ -1,29 +0,0 @@
|
||||
import { Card, Flex, Typography } from 'antd'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const MissingPlaceholder = ({ message }) => {
|
||||
return (
|
||||
<Card size='small'>
|
||||
<Flex
|
||||
justify='center'
|
||||
gap={'small'}
|
||||
style={{ height: '100%' }}
|
||||
align='center'
|
||||
>
|
||||
<Text type='secondary'>
|
||||
<InfoCircleIcon />
|
||||
</Text>
|
||||
<Text type='secondary'>{message}</Text>
|
||||
</Flex>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
MissingPlaceholder.propTypes = {
|
||||
message: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default MissingPlaceholder
|
||||
@ -13,7 +13,6 @@ const NewObjectButtons = ({
|
||||
}) => {
|
||||
return (
|
||||
<Flex justify='end'>
|
||||
{totalSteps > 1 ? (
|
||||
<Button
|
||||
style={{ margin: '0 8px' }}
|
||||
onClick={onPrevious}
|
||||
@ -21,7 +20,6 @@ const NewObjectButtons = ({
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{currentStep < totalSteps - 1 ? (
|
||||
<Button type='primary' disabled={!formValid} onClick={onNext}>
|
||||
|
||||
@ -1,313 +0,0 @@
|
||||
import { useState, useContext, useCallback, useEffect, useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Space,
|
||||
Typography,
|
||||
Flex,
|
||||
Tag,
|
||||
Dropdown,
|
||||
Divider,
|
||||
Modal
|
||||
} from 'antd'
|
||||
import { CaretLeftFilled, LoadingOutlined } from '@ant-design/icons'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import BinIcon from '../../Icons/BinIcon'
|
||||
import PersonIcon from '../../Icons/PersonIcon'
|
||||
import TimeDisplay from './TimeDisplay'
|
||||
import MarkdownDisplay from './MarkdownDisplay'
|
||||
import IdDisplay from './IdDisplay'
|
||||
import MissingPlaceholder from './MissingPlaceholder'
|
||||
import NewNote from '../Management/Notes/NewNote'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const NoteItem = ({ note }) => {
|
||||
const [childNotes, setChildNotes] = useState([])
|
||||
const noteModel = getModelByName('note')
|
||||
const infoAction = noteModel.actions.filter(
|
||||
(action) => action.name == 'info'
|
||||
)[0]
|
||||
const InfoIcon = infoAction.icon
|
||||
const [newNoteOpen, setNewNoteOpen] = useState(false)
|
||||
const [deleteNoteOpen, setDeleteNoteOpen] = useState(false)
|
||||
const [deleteNoteLoading, setDeleteNoteLoading] = useState(false)
|
||||
const [childNotesLoading, setChildNotesLoading] = useState(false)
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const subscribeToObjectTypeUpdatesRef = useRef(null)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const {
|
||||
deleteObject,
|
||||
fetchObjects,
|
||||
subscribeToObjectTypeUpdates,
|
||||
connected
|
||||
} = useContext(ApiServerContext)
|
||||
const { userProfile, token } = useContext(AuthContext)
|
||||
|
||||
let transformValue = 'rotate(0deg)'
|
||||
if (isExpanded) {
|
||||
transformValue = 'rotate(-90deg)'
|
||||
}
|
||||
|
||||
const handleNoteExpand = useCallback(async () => {
|
||||
setChildNotesLoading(true)
|
||||
try {
|
||||
const childNotesData = await fetchObjects('note', {
|
||||
filter: { 'parent._id': note._id }
|
||||
})
|
||||
setChildNotes(childNotesData.data)
|
||||
} catch (error) {
|
||||
console.error('Error fetching child notes:', error)
|
||||
} finally {
|
||||
setChildNotesLoading(false)
|
||||
}
|
||||
}, [note._id, fetchObjects])
|
||||
|
||||
const toggleExpand = async () => {
|
||||
if (isExpanded == false) {
|
||||
await handleNoteExpand()
|
||||
setIsExpanded(true)
|
||||
} else {
|
||||
setChildNotes([])
|
||||
setIsExpanded(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteNote = async () => {
|
||||
if (token != null) {
|
||||
setDeleteNoteLoading(true)
|
||||
await deleteObject(note._id, 'note')
|
||||
setDeleteNoteOpen(false)
|
||||
setDeleteNoteLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (connected == true && subscribeToObjectTypeUpdatesRef.current == null) {
|
||||
if (isExpanded == true) {
|
||||
subscribeToObjectTypeUpdatesRef.current = subscribeToObjectTypeUpdates(
|
||||
'note',
|
||||
(noteData) => {
|
||||
if (noteData.parent._id == note._id) {
|
||||
console.log(
|
||||
'Note note added to parent:',
|
||||
note._id,
|
||||
'isExpanded:',
|
||||
isExpanded
|
||||
)
|
||||
if (isExpanded == true) {
|
||||
handleNoteExpand()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
if (subscribeToObjectTypeUpdatesRef.current) {
|
||||
subscribeToObjectTypeUpdatesRef.current()
|
||||
subscribeToObjectTypeUpdatesRef.current = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (connected == true && subscribeToObjectTypeUpdatesRef.current) {
|
||||
subscribeToObjectTypeUpdatesRef.current()
|
||||
subscribeToObjectTypeUpdatesRef.current = null
|
||||
}
|
||||
}
|
||||
}, [
|
||||
subscribeToObjectTypeUpdates,
|
||||
connected,
|
||||
handleNoteExpand,
|
||||
isExpanded,
|
||||
note._id
|
||||
])
|
||||
|
||||
// Check if the current user can delete this note
|
||||
const canDeleteNote = userProfile && userProfile._id === note.user._id
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
key: 'new',
|
||||
icon: <PlusIcon />,
|
||||
label: 'New Note',
|
||||
onClick: () => {
|
||||
setNewNoteOpen(true)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// Only add delete option if user owns the note
|
||||
if (canDeleteNote) {
|
||||
dropdownItems.push({
|
||||
key: 'delete',
|
||||
label: 'Delete Note',
|
||||
icon: <BinIcon />,
|
||||
onClick: () => {
|
||||
setDeleteNoteOpen(true)
|
||||
},
|
||||
danger: true
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={note._id}
|
||||
size='small'
|
||||
style={{
|
||||
backgroundColor: note.noteType.color + '26',
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
<Flex vertical gap={'small'}>
|
||||
<Flex gap={'middle'} align='start'>
|
||||
<Space>
|
||||
<PersonIcon />
|
||||
<Text style={{ whiteSpace: 'nowrap' }}>{note.user.name}:</Text>
|
||||
</Space>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<MarkdownDisplay content={note.content} />
|
||||
</div>
|
||||
</Flex>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Flex wrap gap={'small'}>
|
||||
<Dropdown
|
||||
menu={{ items: dropdownItems }}
|
||||
trigger={['hover']}
|
||||
placement='bottomLeft'
|
||||
>
|
||||
<Button size='small'>Actions</Button>
|
||||
</Dropdown>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>Type:</Text>
|
||||
<Tag color={note.noteType.color} style={{ margin: 0 }}>
|
||||
{note.noteType.name}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>User ID:</Text>
|
||||
<IdDisplay
|
||||
longId={false}
|
||||
id={note.user._id}
|
||||
type={'user'}
|
||||
showHyperlink={true}
|
||||
/>
|
||||
</Space>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>Created At:</Text>
|
||||
<TimeDisplay dateTime={note.createdAt} showSince={true} />
|
||||
</Space>
|
||||
<Flex style={{ flexGrow: 1 }} justify='end'>
|
||||
<Space size={'small'}>
|
||||
<Button
|
||||
icon={<InfoIcon />}
|
||||
type='text'
|
||||
size='small'
|
||||
onClick={() => {
|
||||
navigate(infoAction.url(note._id))
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={
|
||||
childNotesLoading ? <LoadingOutlined /> : <CaretLeftFilled />
|
||||
}
|
||||
size='small'
|
||||
type='text'
|
||||
loading={childNotesLoading}
|
||||
disabled={childNotesLoading}
|
||||
style={{
|
||||
transform: transformValue,
|
||||
transition: 'transform 0.2s ease'
|
||||
}}
|
||||
onClick={toggleExpand}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{isExpanded && (
|
||||
<Flex vertical gap={'small'} style={{ flexGrow: 1 }}>
|
||||
{childNotes.length > 0 ? (
|
||||
childNotes.map((childNote) => (
|
||||
<NoteItem key={childNote._id} note={childNote} />
|
||||
))
|
||||
) : (
|
||||
<MissingPlaceholder message={'No child notes.'} />
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
<Flex vertical gap={'middle'}></Flex>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newNoteOpen}
|
||||
onCancel={() => {
|
||||
setNewNoteOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
closeIcon={false}
|
||||
destroyOnHidden={true}
|
||||
footer={null}
|
||||
>
|
||||
<NewNote
|
||||
onOk={() => {
|
||||
setNewNoteOpen(false)
|
||||
}}
|
||||
defaultValues={{
|
||||
parent: { _id: note._id },
|
||||
parentType: 'note'
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={deleteNoteOpen}
|
||||
title={
|
||||
<Space size={'middle'}>
|
||||
<ExclamationOctagonIcon />
|
||||
Confirm Delete
|
||||
</Space>
|
||||
}
|
||||
okText='Delete'
|
||||
cancelText='Cancel'
|
||||
okType='danger'
|
||||
closable={false}
|
||||
centered
|
||||
maskClosable={false}
|
||||
footer={[
|
||||
<Button
|
||||
key='cancel'
|
||||
onClick={() => {
|
||||
setDeleteNoteOpen(false)
|
||||
}}
|
||||
disabled={deleteNoteLoading}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key='delete'
|
||||
type='primary'
|
||||
danger
|
||||
onClick={handleDeleteNote}
|
||||
loading={deleteNoteLoading}
|
||||
disabled={deleteNoteLoading}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<Text>Are you sure you want to delete this note?</Text>
|
||||
</Modal>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
NoteItem.propTypes = {
|
||||
note: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default NoteItem
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback, useContext, useEffect, useState, useRef } from 'react'
|
||||
import { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Card,
|
||||
@ -7,35 +7,292 @@ import {
|
||||
Typography,
|
||||
Flex,
|
||||
Modal,
|
||||
Form,
|
||||
Input,
|
||||
Switch,
|
||||
Spin,
|
||||
Alert,
|
||||
message,
|
||||
Divider,
|
||||
Tag,
|
||||
Dropdown
|
||||
} from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import { CaretLeftFilled, LoadingOutlined } from '@ant-design/icons'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import BinIcon from '../../Icons/BinIcon'
|
||||
import PersonIcon from '../../Icons/PersonIcon'
|
||||
import TimeDisplay from './TimeDisplay'
|
||||
import MarkdownDisplay from './MarkdownDisplay'
|
||||
import axios from 'axios'
|
||||
import config from '../../../config'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||
import IdDisplay from './IdDisplay'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import NewNote from '../Management/Notes/NewNote'
|
||||
import NoteItem from './NoteItem'
|
||||
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
|
||||
import ObjectProperty from './ObjectProperty'
|
||||
|
||||
const { Text } = Typography
|
||||
const { Text, Title } = Typography
|
||||
const { TextArea } = Input
|
||||
|
||||
const NotesPanel = ({ _id, type }) => {
|
||||
const NoteItem = ({
|
||||
note,
|
||||
expandedNotes,
|
||||
setExpandedNotes,
|
||||
fetchData,
|
||||
onNewNote,
|
||||
onDeleteNote,
|
||||
userProfile,
|
||||
onChildNoteAdded
|
||||
}) => {
|
||||
const [childNotes, setChildNotes] = useState({})
|
||||
const [loadingChildNotes, setLoadingChildNotes] = useState(null)
|
||||
|
||||
const isExpanded = expandedNotes[note._id]
|
||||
const hasChildNotes = childNotes[note._id] && childNotes[note._id].length > 0
|
||||
const isThisNoteLoading = loadingChildNotes === note._id
|
||||
|
||||
let transformValue = 'rotate(0deg)'
|
||||
if (isExpanded) {
|
||||
transformValue = 'rotate(-90deg)'
|
||||
}
|
||||
|
||||
const handleNoteExpand = async (noteId) => {
|
||||
const newExpandedState = !expandedNotes[noteId]
|
||||
|
||||
setExpandedNotes((prev) => ({
|
||||
...prev,
|
||||
[noteId]: newExpandedState
|
||||
}))
|
||||
|
||||
if (newExpandedState && !childNotes[noteId]) {
|
||||
setLoadingChildNotes(noteId)
|
||||
try {
|
||||
const childNotesData = await fetchData(noteId)
|
||||
setChildNotes((prev) => ({
|
||||
...prev,
|
||||
[noteId]: childNotesData
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('Error fetching child notes:', error)
|
||||
} finally {
|
||||
setLoadingChildNotes(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleNewChildNote = () => {
|
||||
if (onNewNote) {
|
||||
onNewNote(note._id)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteNote = () => {
|
||||
if (onDeleteNote) {
|
||||
onDeleteNote(note._id)
|
||||
}
|
||||
}
|
||||
|
||||
// Reload child notes when a new child note is added
|
||||
const reloadChildNotes = useCallback(async () => {
|
||||
// Always fetch child notes when this function is called
|
||||
// This ensures child notes are loaded even if the parent wasn't expanded before
|
||||
setLoadingChildNotes(note._id)
|
||||
try {
|
||||
const childNotesData = await fetchData(note._id)
|
||||
setChildNotes((prev) => ({
|
||||
...prev,
|
||||
[note._id]: childNotesData
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('Error fetching child notes:', error)
|
||||
} finally {
|
||||
setLoadingChildNotes(null)
|
||||
}
|
||||
}, [fetchData, note._id])
|
||||
|
||||
// Listen for child note additions
|
||||
useEffect(() => {
|
||||
if (onChildNoteAdded) {
|
||||
onChildNoteAdded(note._id, reloadChildNotes)
|
||||
}
|
||||
}, [note._id, onChildNoteAdded, reloadChildNotes])
|
||||
|
||||
// Check if the current user can delete this note
|
||||
const canDeleteNote = userProfile && userProfile._id === note.user._id
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
key: 'new',
|
||||
icon: <PlusIcon />,
|
||||
label: 'New Note',
|
||||
onClick: handleNewChildNote
|
||||
}
|
||||
]
|
||||
|
||||
// Only add delete option if user owns the note
|
||||
if (canDeleteNote) {
|
||||
dropdownItems.push({
|
||||
key: 'delete',
|
||||
label: 'Delete Note',
|
||||
icon: <BinIcon />,
|
||||
onClick: handleDeleteNote,
|
||||
danger: true
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={note._id}
|
||||
size='small'
|
||||
style={{
|
||||
backgroundColor: note.noteType.color + '26',
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
<Flex vertical gap={'small'}>
|
||||
<Flex gap={'middle'} align='start'>
|
||||
<Space>
|
||||
<PersonIcon />
|
||||
<Text style={{ whiteSpace: 'nowrap' }}>{note.user.name}:</Text>
|
||||
</Space>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<MarkdownDisplay content={note.content} />
|
||||
</div>
|
||||
</Flex>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Flex wrap gap={'small'}>
|
||||
<Dropdown
|
||||
menu={{ items: dropdownItems }}
|
||||
trigger={['hover']}
|
||||
placement='bottomLeft'
|
||||
>
|
||||
<Button size='small'>Actions</Button>
|
||||
</Dropdown>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>Type:</Text>
|
||||
<Tag color={note.noteType.color} style={{ margin: 0 }}>
|
||||
{note.noteType.name}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>User ID:</Text>
|
||||
<IdDisplay
|
||||
longId={false}
|
||||
id={note.user._id}
|
||||
type={'user'}
|
||||
showHyperlink={true}
|
||||
/>
|
||||
</Space>
|
||||
<Space size={'small'} style={{ marginRight: 8 }}>
|
||||
<Text type='secondary'>Created At:</Text>
|
||||
<TimeDisplay dateTime={note.createdAt} showSince={true} />
|
||||
</Space>
|
||||
<Flex style={{ flexGrow: 1 }} justify='end'>
|
||||
<Space size={'small'}>
|
||||
<Button
|
||||
icon={
|
||||
isThisNoteLoading ? <LoadingOutlined /> : <CaretLeftFilled />
|
||||
}
|
||||
size='small'
|
||||
type='text'
|
||||
loading={isThisNoteLoading}
|
||||
disabled={isThisNoteLoading}
|
||||
style={{
|
||||
transform: transformValue,
|
||||
transition: 'transform 0.2s ease'
|
||||
}}
|
||||
onClick={() => handleNoteExpand(note._id)}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{isExpanded && (
|
||||
<>
|
||||
<Flex vertical gap={'small'} style={{ flexGrow: 1 }}>
|
||||
{hasChildNotes ? (
|
||||
childNotes[note._id].map((childNote) => (
|
||||
<NoteItem
|
||||
key={childNote._id}
|
||||
note={childNote}
|
||||
expandedNotes={expandedNotes}
|
||||
setExpandedNotes={setExpandedNotes}
|
||||
fetchData={fetchData}
|
||||
onNewNote={onNewNote}
|
||||
onDeleteNote={onDeleteNote}
|
||||
userProfile={userProfile}
|
||||
onChildNoteAdded={onChildNoteAdded}
|
||||
/>
|
||||
))
|
||||
) : !isThisNoteLoading ? (
|
||||
<Card size='small'>
|
||||
<Flex
|
||||
justify='center'
|
||||
gap={'small'}
|
||||
style={{ height: '100%' }}
|
||||
align='center'
|
||||
>
|
||||
<Text type='secondary'>
|
||||
<InfoCircleIcon />
|
||||
</Text>
|
||||
<Text type='secondary'>No child notes.</Text>
|
||||
</Flex>
|
||||
</Card>
|
||||
) : null}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
<Flex vertical gap={'middle'}></Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
NoteItem.propTypes = {
|
||||
note: PropTypes.object.isRequired,
|
||||
expandedNotes: PropTypes.object.isRequired,
|
||||
setExpandedNotes: PropTypes.func.isRequired,
|
||||
fetchData: PropTypes.func.isRequired,
|
||||
onNewNote: PropTypes.func,
|
||||
onDeleteNote: PropTypes.func,
|
||||
userProfile: PropTypes.object,
|
||||
onChildNoteAdded: PropTypes.func
|
||||
}
|
||||
|
||||
const NotesPanel = ({ _id, onNewNote, type }) => {
|
||||
const [newNoteOpen, setNewNoteOpen] = useState(false)
|
||||
const [showMarkdown, setShowMarkdown] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [newNoteFormLoading, setNewNoteFormLoading] = useState(false)
|
||||
const [newNoteFormValues, setNewNoteFormValues] = useState({})
|
||||
const [deleteNoteLoading, setDeleteNoteLoading] = useState(false)
|
||||
const [doneEnabled, setDoneEnabled] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
const [notes, setNotes] = useState(null)
|
||||
const [expandedNotes, setExpandedNotes] = useState({})
|
||||
const subscribeToObjectTypeUpdatesRef = useRef(null)
|
||||
const [newNoteForm] = Form.useForm()
|
||||
const [selectedParentId, setSelectedParentId] = useState(null)
|
||||
const [selectedParentType, setSelectedParentType] = useState(null)
|
||||
const [childNoteCallbacks, setChildNoteCallbacks] = useState({})
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false)
|
||||
const [noteToDelete, setNoteToDelete] = useState(null)
|
||||
|
||||
const { token } = useContext(AuthContext)
|
||||
const { fetchNotes, connected, subscribeToObjectTypeUpdates } =
|
||||
useContext(ApiServerContext)
|
||||
const newNoteFormUpdateValues = Form.useWatch([], newNoteForm)
|
||||
|
||||
useEffect(() => {
|
||||
newNoteForm
|
||||
.validateFields({
|
||||
validateOnly: true
|
||||
})
|
||||
.then(() => setDoneEnabled(true))
|
||||
.catch(() => setDoneEnabled(false))
|
||||
}, [newNoteForm, newNoteFormUpdateValues])
|
||||
|
||||
const { token, userProfile } = useContext(AuthContext)
|
||||
const { fetchNotes } = useContext(ApiServerContext)
|
||||
|
||||
const fetchData = useCallback(
|
||||
async (id) => {
|
||||
@ -50,6 +307,17 @@ const NotesPanel = ({ _id, type }) => {
|
||||
[fetchNotes]
|
||||
)
|
||||
|
||||
const handleNewChildNote = useCallback(
|
||||
(parentId) => {
|
||||
setSelectedParentId(parentId)
|
||||
setSelectedParentType('note')
|
||||
setNewNoteOpen(true)
|
||||
newNoteForm.resetFields()
|
||||
setNewNoteFormValues({})
|
||||
},
|
||||
[newNoteForm]
|
||||
)
|
||||
|
||||
const generateNotes = useCallback(
|
||||
async (id) => {
|
||||
const notesData = await fetchData(id)
|
||||
@ -83,36 +351,113 @@ const NotesPanel = ({ _id, type }) => {
|
||||
note={note}
|
||||
expandedNotes={expandedNotes}
|
||||
setExpandedNotes={setExpandedNotes}
|
||||
fetchData={fetchData}
|
||||
onNewNote={handleNewChildNote}
|
||||
onDeleteNote={handleDeleteNote}
|
||||
userProfile={userProfile}
|
||||
onChildNoteAdded={(noteId, callback) => {
|
||||
setChildNoteCallbacks((prev) => ({
|
||||
...prev,
|
||||
[noteId]: callback
|
||||
}))
|
||||
}}
|
||||
/>
|
||||
))
|
||||
},
|
||||
[fetchData, expandedNotes]
|
||||
[fetchData, expandedNotes, userProfile, handleNewChildNote]
|
||||
)
|
||||
|
||||
const handleReloadData = useCallback(async () => {
|
||||
console.log('GOT RELOAD DATA')
|
||||
setNotes(await generateNotes(_id))
|
||||
}, [_id, generateNotes])
|
||||
const handleNewNote = async () => {
|
||||
setNewNoteFormLoading(true)
|
||||
try {
|
||||
await axios.post(
|
||||
`${config.backendUrl}/notes`,
|
||||
{
|
||||
...newNoteFormValues,
|
||||
parent: selectedParentId,
|
||||
parentType: selectedParentType
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
)
|
||||
setNewNoteOpen(false)
|
||||
messageApi.success('Added a new note.')
|
||||
|
||||
useEffect(() => {
|
||||
if (connected == true && subscribeToObjectTypeUpdatesRef.current == null) {
|
||||
subscribeToObjectTypeUpdatesRef.current = subscribeToObjectTypeUpdates(
|
||||
'note',
|
||||
(noteData) => {
|
||||
if (noteData.parent._id == _id) {
|
||||
console.log('Note note added to parent:', _id)
|
||||
// If this is a child note, expand the parent and reload child notes
|
||||
if (selectedParentId) {
|
||||
// Ensure parent is expanded
|
||||
setExpandedNotes((prev) => ({
|
||||
...prev,
|
||||
[selectedParentId]: true
|
||||
}))
|
||||
|
||||
// Add a small delay to ensure state update has taken effect
|
||||
setTimeout(() => {
|
||||
// Reload child notes for the parent
|
||||
if (childNoteCallbacks[selectedParentId]) {
|
||||
childNoteCallbacks[selectedParentId]()
|
||||
}
|
||||
}, 100)
|
||||
} else {
|
||||
// If it's a top-level note, reload all notes
|
||||
setLoading(true)
|
||||
handleReloadData()
|
||||
}
|
||||
|
||||
setSelectedParentId(null)
|
||||
} catch (error) {
|
||||
messageApi.error('Error creating new note: ' + error.message)
|
||||
} finally {
|
||||
setNewNoteFormLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteNote = async (noteId) => {
|
||||
setNoteToDelete(noteId)
|
||||
setDeleteConfirmOpen(true)
|
||||
}
|
||||
|
||||
const confirmDeleteNote = async () => {
|
||||
if (!noteToDelete) return
|
||||
setDeleteNoteLoading(true)
|
||||
|
||||
try {
|
||||
await axios.delete(`${config.backendUrl}/notes/${noteToDelete}`, {
|
||||
withCredentials: true
|
||||
})
|
||||
messageApi.success('Note deleted successfully.')
|
||||
|
||||
// Reload all top-level notes
|
||||
setLoading(true)
|
||||
handleReloadData()
|
||||
|
||||
// Reload child notes for all expanded parents to ensure UI stays in sync
|
||||
const expandedNoteIds = Object.keys(expandedNotes).filter(
|
||||
(id) => expandedNotes[id]
|
||||
)
|
||||
}
|
||||
return () => {
|
||||
if (connected == true && subscribeToObjectTypeUpdatesRef.current) {
|
||||
subscribeToObjectTypeUpdatesRef.current()
|
||||
subscribeToObjectTypeUpdatesRef.current = null
|
||||
for (const parentId of expandedNoteIds) {
|
||||
if (childNoteCallbacks[parentId]) {
|
||||
childNoteCallbacks[parentId]()
|
||||
}
|
||||
}
|
||||
}, [_id, subscribeToObjectTypeUpdates, connected, handleReloadData])
|
||||
} catch (error) {
|
||||
messageApi.error('Error deleting note: ' + error.message)
|
||||
} finally {
|
||||
setDeleteNoteLoading(false)
|
||||
setDeleteConfirmOpen(false)
|
||||
setNoteToDelete(null)
|
||||
}
|
||||
}
|
||||
|
||||
const cancelDeleteNote = () => {
|
||||
setDeleteConfirmOpen(false)
|
||||
setNoteToDelete(null)
|
||||
}
|
||||
|
||||
const handleReloadData = useCallback(async () => {
|
||||
setNotes(await generateNotes(_id))
|
||||
}, [_id, generateNotes])
|
||||
|
||||
useEffect(() => {
|
||||
if (token != null && !initialized) {
|
||||
@ -121,6 +466,25 @@ const NotesPanel = ({ _id, type }) => {
|
||||
}
|
||||
}, [token, handleReloadData, initialized])
|
||||
|
||||
const handleModalOk = async () => {
|
||||
try {
|
||||
const values = await newNoteForm.validateFields()
|
||||
onNewNote(values)
|
||||
newNoteForm.resetFields()
|
||||
setNewNoteOpen(false)
|
||||
setShowMarkdown(false)
|
||||
} catch (error) {
|
||||
console.error('Validation failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalCancel = () => {
|
||||
newNoteForm.resetFields()
|
||||
setNewNoteOpen(false)
|
||||
setShowMarkdown(false)
|
||||
setSelectedParentId(null)
|
||||
}
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
@ -140,7 +504,11 @@ const NotesPanel = ({ _id, type }) => {
|
||||
setLoading(true)
|
||||
handleReloadData()
|
||||
} else if (key === 'newNote') {
|
||||
setSelectedParentId(_id)
|
||||
setSelectedParentType(type)
|
||||
setNewNoteOpen(true)
|
||||
newNoteForm.resetFields()
|
||||
setNewNoteFormValues({})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,7 +528,11 @@ const NotesPanel = ({ _id, type }) => {
|
||||
icon={<PlusIcon />}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setSelectedParentId(_id)
|
||||
setSelectedParentType(type)
|
||||
setNewNoteOpen(true)
|
||||
newNoteForm.resetFields()
|
||||
setNewNoteFormValues({})
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
@ -180,24 +552,146 @@ const NotesPanel = ({ _id, type }) => {
|
||||
|
||||
<Modal
|
||||
open={newNoteOpen}
|
||||
onOk={handleModalOk}
|
||||
onCancel={handleModalCancel}
|
||||
width={800}
|
||||
closeIcon={false}
|
||||
destroyOnHidden={true}
|
||||
footer={null}
|
||||
onCancel={() => {
|
||||
footer={false}
|
||||
>
|
||||
<Flex vertical gap='large'>
|
||||
<Flex vertical gap='middle'>
|
||||
<Flex align='center' justify='space-between'>
|
||||
<Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
|
||||
New Note
|
||||
</Title>
|
||||
<Space gap={'small'}>
|
||||
<Text type='secondary'>Markdown:</Text>
|
||||
<Switch onChange={setShowMarkdown} size='small' />
|
||||
</Space>
|
||||
</Flex>
|
||||
<Form
|
||||
form={newNoteForm}
|
||||
layout='vertical'
|
||||
onFinish={handleNewNote}
|
||||
initialValues={{ content: '' }}
|
||||
onValuesChange={(changedValues) =>
|
||||
setNewNoteFormValues((prevValues) => ({
|
||||
...prevValues,
|
||||
...changedValues
|
||||
}))
|
||||
}
|
||||
>
|
||||
<Flex vertical gap={'large'}>
|
||||
<Flex gap='middle' wrap>
|
||||
<Form.Item
|
||||
name='content'
|
||||
rules={[{ required: true, message: '' }]}
|
||||
style={{ margin: 0, flexGrow: 1, minWidth: '300px' }}
|
||||
>
|
||||
<TextArea
|
||||
rows={6}
|
||||
placeholder='Enter note content'
|
||||
style={{ resize: 'none' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{showMarkdown && (
|
||||
<Card
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
minWidth: '300px',
|
||||
backgroundColor: () => {
|
||||
if (newNoteFormValues?.noteType?.color) {
|
||||
return newNoteFormValues.noteType.color + '26'
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MarkdownDisplay
|
||||
content={newNoteForm.getFieldValue('content') || ''}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name='noteType'
|
||||
style={{ margin: 0 }}
|
||||
rules={[
|
||||
{ required: true, message: 'Please select a note type' }
|
||||
]}
|
||||
>
|
||||
<ObjectProperty
|
||||
type='object'
|
||||
objectType='noteType'
|
||||
isEditing={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form>
|
||||
</Flex>
|
||||
<Flex justify='end'>
|
||||
<Button
|
||||
style={{ margin: '0 8px' }}
|
||||
disabled={newNoteFormLoading}
|
||||
onClick={() => {
|
||||
setNewNoteOpen(false)
|
||||
}}
|
||||
>
|
||||
<NewNote
|
||||
onOk={() => {
|
||||
setNewNoteOpen(false)
|
||||
messageApi.success('New note added.')
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type='primary'
|
||||
loading={newNoteFormLoading}
|
||||
onClick={() => {
|
||||
newNoteForm.submit()
|
||||
}}
|
||||
reset={newNoteOpen}
|
||||
defaultValues={{
|
||||
parent: { _id },
|
||||
parentType: type
|
||||
}}
|
||||
/>
|
||||
disabled={newNoteFormLoading || !doneEnabled}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
open={deleteConfirmOpen}
|
||||
title={
|
||||
<Space size={'middle'}>
|
||||
<ExclamationOctagonIcon />
|
||||
Confirm Delete
|
||||
</Space>
|
||||
}
|
||||
onOk={confirmDeleteNote}
|
||||
onCancel={cancelDeleteNote}
|
||||
okText='Delete'
|
||||
cancelText='Cancel'
|
||||
okType='danger'
|
||||
closable={false}
|
||||
centered
|
||||
maskClosable={false}
|
||||
footer={[
|
||||
<Button
|
||||
key='cancel'
|
||||
onClick={cancelDeleteNote}
|
||||
disabled={deleteNoteLoading}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key='delete'
|
||||
type='primary'
|
||||
danger
|
||||
onClick={confirmDeleteNote}
|
||||
loading={deleteNoteLoading}
|
||||
disabled={deleteNoteLoading}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<Text>Are you sure you want to delete this note?</Text>
|
||||
</Modal>
|
||||
</Flex>
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Flex, Badge } from 'antd'
|
||||
import { Typography, Flex } from 'antd'
|
||||
import { useState, useEffect, useContext, useCallback } from 'react'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
@ -52,10 +52,9 @@ const ObjectDisplay = ({ object, objectType }) => {
|
||||
const model = getModelByName(objectType)
|
||||
const Icon = model.icon
|
||||
return (
|
||||
<Flex gap={'small'} align='center'>
|
||||
<Flex gap={'small'}>
|
||||
<Icon />
|
||||
{objectData?.color ? <Badge color={objectData?.color} /> : null}
|
||||
<Text ellipsis>{objectData?.name ? objectData.name : null}</Text>
|
||||
<Text>{objectData?.name ? objectData.name : null}</Text>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
@ -8,21 +8,11 @@ const ObjectInfo = ({
|
||||
loading = false,
|
||||
isEditing = false,
|
||||
type = 'unknown',
|
||||
showHyperlink,
|
||||
showLabels = true,
|
||||
objectData = null,
|
||||
properties = [],
|
||||
required = undefined,
|
||||
visibleProperties = {},
|
||||
objectPropertyProps = {},
|
||||
column = {
|
||||
xs: 1,
|
||||
sm: 1,
|
||||
md: 1,
|
||||
lg: 2,
|
||||
xl: 2,
|
||||
xxl: 2
|
||||
},
|
||||
...rest
|
||||
}) => {
|
||||
const allItems = getModelProperties(type)
|
||||
@ -57,29 +47,33 @@ const ObjectInfo = ({
|
||||
const key = item.name || item.label || idx
|
||||
return {
|
||||
key,
|
||||
label:
|
||||
showLabels == true ? (
|
||||
label: (
|
||||
<Flex vertical style={{ height: '100%' }} justify='center'>
|
||||
{item.label}
|
||||
</Flex>
|
||||
) : null,
|
||||
),
|
||||
children: (
|
||||
<ObjectProperty
|
||||
{...item}
|
||||
{...objectPropertyProps}
|
||||
showHyperlink={showHyperlink}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
/>
|
||||
),
|
||||
span: item?.span || undefined
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Spin spinning={loading} indicator={<LoadingOutlined />}>
|
||||
<Descriptions
|
||||
column={column}
|
||||
column={{
|
||||
xs: 1,
|
||||
sm: 1,
|
||||
md: 1,
|
||||
lg: 2,
|
||||
xl: 2,
|
||||
xxl: 2
|
||||
}}
|
||||
bordered={true}
|
||||
{...rest}
|
||||
items={descriptionItems}
|
||||
@ -90,9 +84,6 @@ const ObjectInfo = ({
|
||||
|
||||
ObjectInfo.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
column: PropTypes.object,
|
||||
showHyperlink: PropTypes.bool,
|
||||
showLabels: PropTypes.bool,
|
||||
indicator: PropTypes.node,
|
||||
properties: PropTypes.array,
|
||||
bordered: PropTypes.bool,
|
||||
|
||||
@ -31,7 +31,6 @@ import ObjectList from './ObjectList'
|
||||
import VarianceDisplay from './VarianceDisplay'
|
||||
import OperationDisplay from './OperationDisplay'
|
||||
import MarkdownDisplay from './MarkdownDisplay'
|
||||
import MarkdownInput from './MarkdownInput'
|
||||
import ObjectSelect from './ObjectSelect'
|
||||
import ObjectDisplay from './ObjectDisplay'
|
||||
import ObjectTypeSelect from './ObjectTypeSelect'
|
||||
@ -604,12 +603,6 @@ const ObjectProperty = ({
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
case 'markdown':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<MarkdownInput value={value} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'material':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
|
||||
@ -39,7 +39,7 @@ import CheckIcon from '../../Icons/CheckIcon'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import unionBy from 'lodash/unionBy'
|
||||
import merge from 'lodash/merge'
|
||||
const logger = loglevel.getLogger('DasboardTable')
|
||||
logger.setLevel(config.logLevel)
|
||||
|
||||
@ -88,10 +88,9 @@ const ObjectTable = forwardRef(
|
||||
const [lazyLoading, setLazyLoading] = useState(false)
|
||||
|
||||
const subscribedIdsRef = useRef([])
|
||||
// const [typeSubscribed, setTypeSubscribed] = useState(false)
|
||||
const [typeSubscribed, setTypeSubscribed] = useState(false)
|
||||
const unsubscribesRef = useRef([])
|
||||
const updateEventHandlerRef = useRef()
|
||||
const subscribeToObjectTypeUpdatesRef = useRef(null)
|
||||
|
||||
const rowActions =
|
||||
model.actions?.filter((action) => action.row == true) || []
|
||||
@ -292,17 +291,12 @@ const ObjectTable = forwardRef(
|
||||
const updateEventHandler = useCallback((id, updatedData) => {
|
||||
console.log('GOT UPDATE FOR', id)
|
||||
setPages((prevPages) =>
|
||||
prevPages.map((page) => {
|
||||
const updatedItems = unionBy(
|
||||
[{ ...updatedData, _id: id }],
|
||||
page.items,
|
||||
'_id'
|
||||
)
|
||||
return {
|
||||
prevPages.map((page) => ({
|
||||
...page,
|
||||
items: updatedItems
|
||||
}
|
||||
items: page.items.map((item) => {
|
||||
return item._id === id ? merge({}, item, updatedData) : item
|
||||
})
|
||||
}))
|
||||
)
|
||||
}, [])
|
||||
|
||||
@ -369,9 +363,6 @@ const ObjectTable = forwardRef(
|
||||
// Cleanup effect for component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
console.log('API: Call unsub', connected)
|
||||
if (connected == true && unsubscribesRef.current) {
|
||||
console.log('API: Got object unsub')
|
||||
// Clean up all subscriptions when component unmounts
|
||||
unsubscribesRef.current.forEach((unsubscribe) => {
|
||||
console.log('CALLING UNSUB on unmount')
|
||||
@ -379,33 +370,26 @@ const ObjectTable = forwardRef(
|
||||
})
|
||||
unsubscribesRef.current = []
|
||||
subscribedIdsRef.current = []
|
||||
|
||||
// Clean up type subscription
|
||||
}
|
||||
if (connected == true && subscribeToObjectTypeUpdatesRef.current) {
|
||||
console.log('UNSUBBING type subscription on unmount')
|
||||
console.log('API: Got type unsub')
|
||||
subscribeToObjectTypeUpdatesRef.current()
|
||||
subscribeToObjectTypeUpdatesRef.current = null
|
||||
}
|
||||
}
|
||||
}, [connected])
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
connected == true &&
|
||||
subscribeToObjectTypeUpdatesRef.current == null
|
||||
) {
|
||||
console.log(
|
||||
'API: Subbing to updates',
|
||||
subscribeToObjectTypeUpdatesRef.current
|
||||
)
|
||||
subscribeToObjectTypeUpdatesRef.current = subscribeToObjectTypeUpdates(
|
||||
type,
|
||||
newEventHandler
|
||||
)
|
||||
if (connected == true && typeSubscribed == false) {
|
||||
const unsubscribe = subscribeToObjectTypeUpdates(type, newEventHandler)
|
||||
setTypeSubscribed(true)
|
||||
return () => {
|
||||
if (unsubscribe && typeSubscribed == true) {
|
||||
unsubscribe()
|
||||
}
|
||||
}, [type, subscribeToObjectTypeUpdates, connected, newEventHandler])
|
||||
}
|
||||
}
|
||||
}, [
|
||||
type,
|
||||
subscribeToObjectTypeUpdates,
|
||||
connected,
|
||||
newEventHandler,
|
||||
typeSubscribed
|
||||
])
|
||||
|
||||
const updateData = useCallback(
|
||||
(_id, updatedData) => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// PrinterTemperaturePanel.js
|
||||
import { useContext, useState, useEffect } from 'react'
|
||||
import { useContext, useState, useEffect, useCallback } from 'react'
|
||||
import {
|
||||
Progress,
|
||||
Typography,
|
||||
@ -11,10 +11,9 @@ import {
|
||||
Button
|
||||
} from 'antd'
|
||||
import { LoadingOutlined, CaretLeftOutlined } from '@ant-design/icons'
|
||||
import { PrintServerContext } from '../context/PrintServerContext'
|
||||
import styled from 'styled-components'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
const { Text } = Typography
|
||||
const { Panel } = Collapse
|
||||
@ -31,69 +30,90 @@ const CustomCollapse = styled(Collapse)`
|
||||
`
|
||||
|
||||
const PrinterTemperaturePanel = ({
|
||||
id,
|
||||
showExtruderControls = true,
|
||||
showBedControls = true,
|
||||
showExtruder = true,
|
||||
showBed = true,
|
||||
showMoreInfo = true
|
||||
printerId,
|
||||
showHotEndControls = true,
|
||||
showHeatedBedControls = true,
|
||||
showHotEnd = true,
|
||||
showHeatedBed = true,
|
||||
showMoreInfo = true,
|
||||
shouldUnsubscribe = true
|
||||
}) => {
|
||||
const [temperatureData, setTemperatureData] = useState({
|
||||
extruder: {
|
||||
current: 0,
|
||||
target: 0,
|
||||
power: 0
|
||||
},
|
||||
bed: {
|
||||
current: 0,
|
||||
target: 0,
|
||||
power: 0
|
||||
},
|
||||
pinda: 0,
|
||||
ambiant: 0
|
||||
hotEnd: {},
|
||||
heatedBed: {}
|
||||
})
|
||||
|
||||
const { subscribeToObjectEvent, connected } = useContext(ApiServerContext)
|
||||
|
||||
// Sync input values with actual temperature targets
|
||||
useEffect(() => {
|
||||
if (temperatureData.extruder?.target !== undefined) {
|
||||
setExtruderTarget(temperatureData.extruder.target)
|
||||
}
|
||||
}, [temperatureData.extruder?.target])
|
||||
|
||||
useEffect(() => {
|
||||
if (temperatureData.bed?.target !== undefined) {
|
||||
setBedTarget(temperatureData.bed.target)
|
||||
}
|
||||
}, [temperatureData.bed?.target])
|
||||
|
||||
useEffect(() => {
|
||||
if (id && connected) {
|
||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
||||
id,
|
||||
'printer',
|
||||
'temperature',
|
||||
(event) => {
|
||||
setTemperatureData((prev) => {
|
||||
const merged = merge({}, prev, event.data)
|
||||
return merged
|
||||
})
|
||||
}
|
||||
const [hotEndTemperature, setHotEndTemperature] = useState(
|
||||
temperatureData?.hotEnd?.target || 0
|
||||
)
|
||||
const [heatedBedTemperature, setHeatedBedTemperature] = useState(
|
||||
temperatureData?.heatedBed?.target || 0
|
||||
)
|
||||
const { printServer } = useContext(PrintServerContext)
|
||||
|
||||
const notifyTemperatureStatusUpdate = useCallback((statusUpdate) => {
|
||||
setTemperatureData((prev) => {
|
||||
const temperatureObject = {
|
||||
...prev
|
||||
}
|
||||
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||
temperatureObject.hotEnd.current = statusUpdate?.extruder?.temperature
|
||||
}
|
||||
|
||||
if (statusUpdate?.heater_bed?.temperature !== undefined) {
|
||||
temperatureObject.heatedBed.current =
|
||||
statusUpdate?.heater_bed?.temperature
|
||||
}
|
||||
|
||||
if (statusUpdate?.extruder?.target !== undefined) {
|
||||
temperatureObject.hotEnd.target = statusUpdate?.extruder?.target
|
||||
setHotEndTemperature(statusUpdate?.extruder?.target)
|
||||
}
|
||||
|
||||
if (statusUpdate?.heater_bed?.target !== undefined) {
|
||||
temperatureObject.heatedBed.target = statusUpdate?.heater_bed?.target
|
||||
setHeatedBedTemperature(statusUpdate?.heater_bed?.target)
|
||||
}
|
||||
|
||||
if (statusUpdate?.extruder?.power !== undefined) {
|
||||
temperatureObject.hotEnd.power = statusUpdate?.extruder?.power
|
||||
}
|
||||
|
||||
if (statusUpdate?.heater_bed?.power !== undefined) {
|
||||
temperatureObject.heatedBed.power = statusUpdate?.heater_bed?.power
|
||||
}
|
||||
|
||||
return temperatureObject
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const params = {
|
||||
printerId,
|
||||
objects: {
|
||||
extruder: null,
|
||||
heater_bed: null // eslint-disable-line
|
||||
}
|
||||
}
|
||||
if (printServer?.connected == true) {
|
||||
printServer.emit('printer.objects.subscribe', params)
|
||||
printServer.emit('printer.objects.query', params)
|
||||
printServer.on('notify_status_update', notifyTemperatureStatusUpdate)
|
||||
}
|
||||
return () => {
|
||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
||||
if (printServer && shouldUnsubscribe == true) {
|
||||
printServer.off('notify_status_update', notifyTemperatureStatusUpdate)
|
||||
printServer.emit('printer.objects.unsubscribe', params)
|
||||
}
|
||||
}
|
||||
}, [id, connected])
|
||||
}, [printServer, printerId, notifyTemperatureStatusUpdate, shouldUnsubscribe])
|
||||
|
||||
const [extruderTarget, setExtruderTarget] = useState(0)
|
||||
const [bedTarget, setBedTarget] = useState(0)
|
||||
|
||||
const handleSetTemperature = (data) => {
|
||||
if (id && connected == true) {
|
||||
console.log(data)
|
||||
//sendObjectAction(id, 'printer', { type: 'setTemperature', data })
|
||||
const handleSetTemperatureClick = (target, value) => {
|
||||
if (printServer) {
|
||||
printServer.emit('printer.gcode.script', {
|
||||
printerId,
|
||||
script: `SET_HEATER_TEMPERATURE HEATER=${target} TARGET=${value}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,32 +126,33 @@ const PrinterTemperaturePanel = ({
|
||||
<Flex vertical gap={0}>
|
||||
<Text>
|
||||
Hot End Power:{' '}
|
||||
{Math.round((temperatureData.extruder.power || 0) * 100)}%
|
||||
{Math.round((temperatureData.hotEnd.power || 0) * 100)}%
|
||||
</Text>
|
||||
<Progress
|
||||
percent={(temperatureData.extruder.power || 0) * 100}
|
||||
percent={(temperatureData.hotEnd.power || 0) * 100}
|
||||
showInfo={false}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex vertical gap={0}>
|
||||
<Text>
|
||||
Bed Power: {Math.round((temperatureData.bed.power || 0) * 100)}%
|
||||
Bed Power:{' '}
|
||||
{Math.round((temperatureData.heatedBed.power || 0) * 100)}%
|
||||
</Text>
|
||||
<Progress
|
||||
percent={(temperatureData.bed.power || 0) * 100}
|
||||
percent={(temperatureData.heatedBed.power || 0) * 100}
|
||||
showInfo={false}
|
||||
/>
|
||||
</Flex>
|
||||
<Panel>
|
||||
{typeof temperatureData.pinda !== 'undefined' && (
|
||||
{typeof temperatureData.pindaTemp !== 'undefined' && (
|
||||
<Flex vertical gap={0}>
|
||||
<Text>Pinda Temp: {temperatureData.pindaTemp}°C</Text>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{typeof temperatureData.ambiant !== 'undefined' && (
|
||||
{typeof temperatureData.ambiantActual !== 'undefined' && (
|
||||
<Flex vertical gap={0}>
|
||||
<Text>Ambient Actual: {temperatureData.ambiant}°C</Text>
|
||||
<Text>Ambient Actual: {temperatureData.ambiantActual}°C</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Panel>
|
||||
@ -144,41 +165,41 @@ const PrinterTemperaturePanel = ({
|
||||
<div style={{ minWidth: 190 }}>
|
||||
{temperatureData ? (
|
||||
<Flex vertical gap='middle'>
|
||||
{temperatureData.extruder && showExtruder && (
|
||||
{temperatureData.hotEnd && showHotEnd && (
|
||||
<Flex vertical gap={0}>
|
||||
<Text>
|
||||
Hot End: {temperatureData.extruder.current}°C /{' '}
|
||||
{temperatureData.extruder.target}°C
|
||||
Hot End: {temperatureData.hotEnd.current}°C /{' '}
|
||||
{temperatureData.hotEnd.target}°C
|
||||
</Text>
|
||||
<Progress
|
||||
percent={(temperatureData.extruder.target / 300) * 100}
|
||||
percent={(temperatureData.hotEnd.target / 300) * 100}
|
||||
strokeColor='#FF392F1D'
|
||||
success={{
|
||||
percent: (temperatureData.extruder.current / 300) * 100,
|
||||
percent: (temperatureData.hotEnd.current / 300) * 100,
|
||||
strokeColor: '#FF3B2F'
|
||||
}}
|
||||
showInfo={false}
|
||||
/>
|
||||
{showExtruderControls && (
|
||||
{showHotEndControls && (
|
||||
<Space direction='horizontal' style={{ marginTop: 5 }}>
|
||||
<Space.Compact block size='small'>
|
||||
<InputNumber
|
||||
value={extruderTarget}
|
||||
value={hotEndTemperature}
|
||||
min={0}
|
||||
max={300}
|
||||
style={{ width: '120px' }}
|
||||
addonAfter='°C'
|
||||
onChange={(value) => setExtruderTarget(value || 0)}
|
||||
onPressEnter={handleSetTemperature({
|
||||
extruder: { target: extruderTarget }
|
||||
})}
|
||||
onChange={(value) => setHotEndTemperature(value)}
|
||||
onPressEnter={() =>
|
||||
handleSetTemperatureClick('extruder', hotEndTemperature)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='default'
|
||||
style={{ width: 40 }}
|
||||
onClick={handleSetTemperature({
|
||||
extruder: { target: extruderTarget }
|
||||
})}
|
||||
onClick={() =>
|
||||
handleSetTemperatureClick('extruder', hotEndTemperature)
|
||||
}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
@ -187,9 +208,7 @@ const PrinterTemperaturePanel = ({
|
||||
type='default'
|
||||
size='small'
|
||||
style={{ width: 40 }}
|
||||
onClick={() =>
|
||||
handleSetTemperature({ extruder: { target: 0 } })
|
||||
}
|
||||
onClick={() => handleSetTemperatureClick('extruder', 0)}
|
||||
>
|
||||
Off
|
||||
</Button>
|
||||
@ -198,44 +217,50 @@ const PrinterTemperaturePanel = ({
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{temperatureData.bed && showBed && (
|
||||
{temperatureData.heatedBed && showHeatedBed && (
|
||||
<Flex vertical gap={0}>
|
||||
<Text>
|
||||
Heated Bed: {temperatureData.bed.current}°C /{' '}
|
||||
{temperatureData.bed.target}°C
|
||||
Heated Bed: {temperatureData.heatedBed.current}°C /{' '}
|
||||
{temperatureData.heatedBed.target}°C
|
||||
</Text>
|
||||
<Progress
|
||||
percent={(temperatureData.bed.target / 300) * 100}
|
||||
percent={(temperatureData.heatedBed.target / 300) * 100}
|
||||
strokeColor='#FF392F1D'
|
||||
success={{
|
||||
percent: (temperatureData.bed.current / 300) * 100,
|
||||
percent: (temperatureData.heatedBed.current / 300) * 100,
|
||||
strokeColor: '#FF3B2F'
|
||||
}}
|
||||
showInfo={false}
|
||||
/>
|
||||
{showBedControls && (
|
||||
{showHeatedBedControls && (
|
||||
<Space
|
||||
direction='horizontal'
|
||||
style={{ marginTop: 5, height: '21px' }}
|
||||
>
|
||||
<Space.Compact block size='small'>
|
||||
<InputNumber
|
||||
value={bedTarget}
|
||||
value={heatedBedTemperature}
|
||||
min={0}
|
||||
max={300}
|
||||
style={{ width: '120px' }}
|
||||
addonAfter='°C'
|
||||
onChange={(value) => setBedTarget(value || 0)}
|
||||
onPressEnter={handleSetTemperature({
|
||||
bed: { target: bedTarget }
|
||||
})}
|
||||
onChange={(value) => setHeatedBedTemperature(value)}
|
||||
onPressEnter={() =>
|
||||
handleSetTemperatureClick(
|
||||
'heater_bed',
|
||||
heatedBedTemperature
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='default'
|
||||
style={{ width: 40 }}
|
||||
onClick={handleSetTemperature({
|
||||
bed: { target: bedTarget }
|
||||
})}
|
||||
onClick={() =>
|
||||
handleSetTemperatureClick(
|
||||
'heater_bed',
|
||||
heatedBedTemperature
|
||||
)
|
||||
}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
@ -244,11 +269,7 @@ const PrinterTemperaturePanel = ({
|
||||
type='default'
|
||||
size='small'
|
||||
style={{ width: 40 }}
|
||||
onClick={() =>
|
||||
handleSetTemperature({
|
||||
bed: { target: 0 }
|
||||
})
|
||||
}
|
||||
onClick={() => handleSetTemperatureClick('heater_bed', 0)}
|
||||
>
|
||||
Off
|
||||
</Button>
|
||||
@ -277,11 +298,11 @@ const PrinterTemperaturePanel = ({
|
||||
}
|
||||
|
||||
PrinterTemperaturePanel.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
showExtruderControls: PropTypes.bool,
|
||||
showBedControls: PropTypes.bool,
|
||||
showExtruder: PropTypes.bool,
|
||||
showBed: PropTypes.bool,
|
||||
printerId: PropTypes.string.isRequired,
|
||||
showHotEndControls: PropTypes.bool,
|
||||
showHeatedBedControls: PropTypes.bool,
|
||||
showHotEnd: PropTypes.bool,
|
||||
showHeatedBed: PropTypes.bool,
|
||||
showMoreInfo: PropTypes.bool,
|
||||
shouldUnsubscribe: PropTypes.bool
|
||||
}
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||
import NewObjectButtons from './NewObjectButtons'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const WizardView = ({
|
||||
showSteps = true,
|
||||
steps,
|
||||
title = 'Wizard View',
|
||||
onSubmit,
|
||||
formValid,
|
||||
loading
|
||||
}) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
|
||||
return (
|
||||
<Flex gap='middle'>
|
||||
{!isMobile && showSteps == true ? (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isMobile && showSteps == true ? (
|
||||
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||
) : null}
|
||||
|
||||
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
{title}
|
||||
</Title>
|
||||
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||
{steps[currentStep].content}
|
||||
</div>
|
||||
<NewObjectButtons
|
||||
currentStep={currentStep}
|
||||
totalSteps={steps.length}
|
||||
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||
onSubmit={onSubmit}
|
||||
formValid={formValid}
|
||||
submitLoading={loading}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
WizardView.propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
formValid: PropTypes.bool.isRequired,
|
||||
steps: PropTypes.array.isRequired,
|
||||
showSteps: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
}
|
||||
|
||||
export default WizardView
|
||||
@ -93,9 +93,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
})
|
||||
|
||||
newSocket.on('objectUpdate', handleObjectUpdate)
|
||||
newSocket.on('objectEvent', handleObjectEvent)
|
||||
newSocket.on('objectNew', handleObjectNew)
|
||||
newSocket.on('objectDelete', handleObjectDelete)
|
||||
newSocket.on('lockUpdate', handleLockUpdate)
|
||||
|
||||
newSocket.on('disconnect', () => {
|
||||
@ -218,37 +216,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleObjectEvent = async (data) => {
|
||||
const id = data._id
|
||||
const objectType = data.objectType
|
||||
|
||||
const callbacksRefKey = `${objectType}:${id}:events:${data.event.type}`
|
||||
logger.debug('Notifying object event:', data)
|
||||
if (
|
||||
id &&
|
||||
objectType &&
|
||||
subscribedCallbacksRef.current.has(callbacksRefKey)
|
||||
) {
|
||||
const callbacks = subscribedCallbacksRef.current.get(callbacksRefKey)
|
||||
logger.debug(
|
||||
`Calling ${callbacks.length} callbacks for object:`,
|
||||
callbacksRefKey
|
||||
)
|
||||
callbacks.forEach((callback) => {
|
||||
try {
|
||||
callback(data.event)
|
||||
} catch (error) {
|
||||
logger.error('Error in object event callback:', error)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
logger.debug(
|
||||
`No callbacks found for object: ${callbacksRefKey}, subscribed callbacks:`,
|
||||
Array.from(subscribedCallbacksRef.current.keys())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleObjectNew = async (data) => {
|
||||
logger.debug('Notifying object new:', data)
|
||||
const objectType = data.objectType || 'unknown'
|
||||
@ -274,31 +241,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleObjectDelete = async (data) => {
|
||||
logger.debug('Notifying object delete:', data)
|
||||
const objectType = data.objectType || 'unknown'
|
||||
|
||||
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
|
||||
const callbacks = subscribedCallbacksRef.current.get(objectType)
|
||||
logger.debug(
|
||||
`Calling ${callbacks.length} callbacks for type:`,
|
||||
objectType
|
||||
)
|
||||
callbacks.forEach((callback) => {
|
||||
try {
|
||||
callback(data.object)
|
||||
} catch (error) {
|
||||
logger.error('Error in object new callback:', error)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
logger.debug(
|
||||
`No callbacks found for object: ${objectType}, subscribed callbacks:`,
|
||||
Array.from(subscribedCallbacksRef.current.keys())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const offObjectUpdatesEvent = useCallback((id, objectType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
const callbacksRefKey = `${objectType}:${id}`
|
||||
@ -309,10 +251,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
.filter((cb) => cb !== callback)
|
||||
if (callbacks.length === 0) {
|
||||
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
||||
socketRef.current.emit('unsubscribeObjectUpdate', {
|
||||
id: id,
|
||||
objectType: objectType
|
||||
})
|
||||
socketRef.current.emit('unsubscribe', { id: id, type: objectType })
|
||||
} else {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, callbacks)
|
||||
}
|
||||
@ -323,22 +262,14 @@ const ApiServerProvider = ({ children }) => {
|
||||
const offObjectTypeUpdatesEvent = useCallback((objectType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
// Remove callback from the subscribed callbacks map
|
||||
console.log(
|
||||
'Unsubscribing from type',
|
||||
objectType,
|
||||
subscribedCallbacksRef.current.has(objectType)
|
||||
)
|
||||
console.log('Unsubscribing from type')
|
||||
if (subscribedCallbacksRef.current.has(objectType)) {
|
||||
const callbacks = subscribedCallbacksRef.current
|
||||
.get(objectType)
|
||||
.filter((cb) => cb !== callback)
|
||||
console.log('API: CALLBACKS', callbacks)
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
subscribedCallbacksRef.current.delete(objectType)
|
||||
socketRef.current.emit('unsubscribeObjectTypeUpdate', {
|
||||
objectType: objectType
|
||||
})
|
||||
socketRef.current.emit('unsubscribe', { objectType: objectType })
|
||||
} else {
|
||||
subscribedCallbacksRef.current.set(objectType, callbacks)
|
||||
}
|
||||
@ -411,7 +342,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
)
|
||||
logger.debug('Registered type event listener for object:', objectType)
|
||||
logger.debug('Registered update event listener for object:', objectType)
|
||||
|
||||
// Return cleanup function
|
||||
return () => offObjectTypeUpdatesEvent(objectType, callback)
|
||||
@ -438,84 +369,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const offObjectEventEvent = useCallback(
|
||||
(id, objectType, eventType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
const callbacksRefKey = `${objectType}:${id}:events:${eventType}`
|
||||
// Remove callback from the subscribed callbacks map
|
||||
if (subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
const callbacks = subscribedCallbacksRef.current
|
||||
.get(callbacksRefKey)
|
||||
.filter((cb) => cb !== callback)
|
||||
if (callbacks.length === 0) {
|
||||
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
||||
socketRef.current.emit('unsubscribeObjectEvent', {
|
||||
_id: id,
|
||||
objectType,
|
||||
eventType
|
||||
})
|
||||
} else {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, callbacks)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const subscribeToObjectEvent = useCallback(
|
||||
(id, objectType, eventType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
const callbacksRefKey = `${objectType}:${id}:events:${eventType}`
|
||||
// Add callback to the subscribed callbacks map immediately
|
||||
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, [])
|
||||
}
|
||||
|
||||
const callbacksLength =
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).length
|
||||
|
||||
if (callbacksLength <= 0) {
|
||||
socketRef.current.emit(
|
||||
'subscribeToObjectEvent',
|
||||
{
|
||||
_id: id,
|
||||
objectType: objectType,
|
||||
eventType: eventType
|
||||
},
|
||||
(result) => {
|
||||
if (result.success) {
|
||||
logger.info(
|
||||
'Subscribed to event id:',
|
||||
id,
|
||||
'objectType:',
|
||||
objectType,
|
||||
'eventType:',
|
||||
eventType
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
logger.info(
|
||||
'Adding event callback id:',
|
||||
id,
|
||||
'objectType:',
|
||||
objectType,
|
||||
'eventType:',
|
||||
eventType,
|
||||
'callbacks length:',
|
||||
callbacksLength + 1
|
||||
)
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
|
||||
|
||||
// Return cleanup function
|
||||
return () => offObjectEventEvent(id, objectType, eventType, callback)
|
||||
}
|
||||
},
|
||||
[offObjectUpdatesEvent]
|
||||
)
|
||||
|
||||
const subscribeToObjectLock = useCallback(
|
||||
(id, type, callback) => {
|
||||
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
||||
@ -695,6 +548,13 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
})
|
||||
logger.debug('Object updated successfully')
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
await socketRef.current.emit('update', {
|
||||
_id: id,
|
||||
type: type,
|
||||
updatedAt: response.data.updatedAt
|
||||
})
|
||||
}
|
||||
return response.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@ -717,6 +577,13 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
})
|
||||
logger.debug('Object deleted successfully')
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
await socketRef.current.emit('update', {
|
||||
_id: id,
|
||||
type: type,
|
||||
updatedAt: response.data.updatedAt
|
||||
})
|
||||
}
|
||||
return response.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@ -782,7 +649,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
try {
|
||||
const response = await axios.get(`${config.backendUrl}/notes`, {
|
||||
params: {
|
||||
'parent._id': parentId,
|
||||
parent: parentId,
|
||||
sort: 'createdAt',
|
||||
order: 'ascend'
|
||||
},
|
||||
@ -888,7 +755,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
createObject,
|
||||
deleteObject,
|
||||
subscribeToObjectUpdates,
|
||||
subscribeToObjectEvent,
|
||||
subscribeToObjectTypeUpdates,
|
||||
subscribeToObjectLock,
|
||||
fetchObject,
|
||||
|
||||
@ -30,6 +30,13 @@ export const Note = {
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'noteType',
|
||||
label: 'Note Type',
|
||||
type: 'object',
|
||||
objectType: 'noteType',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'parent._id',
|
||||
label: 'Parent ID',
|
||||
@ -37,23 +44,7 @@ export const Note = {
|
||||
objectType: (objectData) => {
|
||||
return objectData.parentType
|
||||
},
|
||||
showHyperlink: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Content',
|
||||
type: 'markdown',
|
||||
required: true,
|
||||
span: 2
|
||||
},
|
||||
{
|
||||
name: 'noteType',
|
||||
label: 'Note Type',
|
||||
type: 'object',
|
||||
objectType: 'noteType',
|
||||
showHyperlink: true,
|
||||
required: true
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'noteType._id',
|
||||
@ -67,6 +58,11 @@ export const Note = {
|
||||
type: 'id',
|
||||
objectType: 'user'
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Content',
|
||||
type: 'markdown'
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
|
||||
@ -143,21 +143,6 @@ export const Printer = {
|
||||
type: 'text',
|
||||
required: false,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'currentFilamentStock',
|
||||
label: 'Filament Stock',
|
||||
type: 'object',
|
||||
objectType: 'filamentStock',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'currentFilamentStock._id',
|
||||
label: 'Filament Stock ID',
|
||||
type: 'id',
|
||||
objectType: 'filamentStock',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ import Settings from '../components/Dashboard/Management/Settings'
|
||||
import AuditLogs from '../components/Dashboard/Management/AuditLogs.jsx'
|
||||
import NoteTypes from '../components/Dashboard/Management/NoteTypes.jsx'
|
||||
import NoteTypeInfo from '../components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx'
|
||||
import NoteInfo from '../components/Dashboard/Management/Notes/NoteInfo.jsx'
|
||||
import Users from '../components/Dashboard/Management/Users.jsx'
|
||||
import UserInfo from '../components/Dashboard/Management/Users/UserInfo.jsx'
|
||||
import Hosts from '../components/Dashboard/Management/Hosts.jsx'
|
||||
@ -69,7 +68,6 @@ const ManagementRoutes = [
|
||||
path='management/notetypes/info'
|
||||
element={<NoteTypeInfo />}
|
||||
/>,
|
||||
<Route key='note-info' path='management/notes/info' element={<NoteInfo />} />,
|
||||
<Route
|
||||
key='documentsizes'
|
||||
path='management/documentsizes'
|
||||
|
||||
364
yarn.lock
364
yarn.lock
@ -724,7 +724,7 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.27.1"
|
||||
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0":
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.3.tgz#75c5034b55ba868121668be5d5bb31cc64e6e61a"
|
||||
integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==
|
||||
@ -759,6 +759,33 @@
|
||||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
|
||||
"@chevrotain/cst-dts-gen@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz#922ebd8cc59d97241bb01b1b17561a5c1ae0124e"
|
||||
integrity sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==
|
||||
dependencies:
|
||||
"@chevrotain/gast" "10.5.0"
|
||||
"@chevrotain/types" "10.5.0"
|
||||
lodash "4.17.21"
|
||||
|
||||
"@chevrotain/gast@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@chevrotain/gast/-/gast-10.5.0.tgz#e4e614bc46d17a8892742f38e56cd33f1f3ad162"
|
||||
integrity sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==
|
||||
dependencies:
|
||||
"@chevrotain/types" "10.5.0"
|
||||
lodash "4.17.21"
|
||||
|
||||
"@chevrotain/types@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-10.5.0.tgz#52a97d74a8cfbc197f054636d93ecd8912d33d21"
|
||||
integrity sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==
|
||||
|
||||
"@chevrotain/utils@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.5.0.tgz#0ee36f65b49b447fbac71b9e5af5c5c6c98ac057"
|
||||
integrity sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==
|
||||
|
||||
"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.3.2", "@codemirror/autocomplete@^6.7.1":
|
||||
version "6.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz#de26e864a1ec8192a1b241eb86addbb612964ddb"
|
||||
@ -1891,10 +1918,27 @@
|
||||
rc-resize-observer "^1.3.1"
|
||||
rc-util "^5.44.0"
|
||||
|
||||
"@rolldown/pluginutils@1.0.0-beta.34":
|
||||
version "1.0.0-beta.34"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.34.tgz#4421645c676926faa4574940d72fa7ce0ec7d419"
|
||||
integrity sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==
|
||||
"@react-three/fiber@^8.15.5":
|
||||
version "8.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.18.0.tgz#73bfa12c82dabf889b572f9dfeefb10485532dd1"
|
||||
integrity sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.8"
|
||||
"@types/react-reconciler" "^0.26.7"
|
||||
"@types/webxr" "*"
|
||||
base64-js "^1.5.1"
|
||||
buffer "^6.0.3"
|
||||
its-fine "^1.0.6"
|
||||
react-reconciler "^0.27.0"
|
||||
react-use-measure "^2.1.7"
|
||||
scheduler "^0.21.0"
|
||||
suspend-react "^0.1.3"
|
||||
zustand "^3.7.1"
|
||||
|
||||
"@rolldown/pluginutils@1.0.0-beta.32":
|
||||
version "1.0.0-beta.32"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz#40a51db68a3c967183a9d58c5f5179adf6214cce"
|
||||
integrity sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==
|
||||
|
||||
"@rollup/pluginutils@^4.2.1":
|
||||
version "4.2.1"
|
||||
@ -1904,7 +1948,7 @@
|
||||
estree-walker "^2.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@rollup/pluginutils@^5.2.0":
|
||||
"@rollup/pluginutils@^5.1.3":
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.2.0.tgz#eac25ca5b0bdda4ba735ddaca5fbf26bd435f602"
|
||||
integrity sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==
|
||||
@ -2748,6 +2792,11 @@
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/offscreencanvas@^2019.6.4":
|
||||
version "2019.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz#90267db13f64d6e9ccb5ae3eac92786a7c77a516"
|
||||
integrity sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
|
||||
@ -2761,6 +2810,25 @@
|
||||
"@types/node" "*"
|
||||
xmlbuilder ">=11.0.1"
|
||||
|
||||
"@types/react-reconciler@^0.26.7":
|
||||
version "0.26.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36"
|
||||
integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-reconciler@^0.28.0":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz#d24b4864c384e770c83275b3fe73fba00269c83b"
|
||||
integrity sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==
|
||||
|
||||
"@types/react@*":
|
||||
version "19.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.11.tgz#a64d8ec1769fc861d22f54e6e9f360ed67b54dc8"
|
||||
integrity sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==
|
||||
dependencies:
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/responselike@^1.0.0":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
|
||||
@ -2788,6 +2856,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.11.tgz#d3d6b418978c8aa202d41e5bb3483227b6ecc1bb"
|
||||
integrity sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==
|
||||
|
||||
"@types/webxr@*":
|
||||
version "0.5.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.22.tgz#d8a14c12bbfaaa4a13de21ec2d4a8197b3e1b532"
|
||||
integrity sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A==
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999"
|
||||
@ -2871,18 +2944,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
|
||||
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
|
||||
|
||||
"@vitejs/plugin-react@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.0.2.tgz#3b5d73fc0e4370a0fafe27154d2c208e2bca8f71"
|
||||
integrity sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==
|
||||
"@vitejs/plugin-react@^5.0.1":
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.0.1.tgz#02c074f3fd9a82e4676c51e0fe9603a26ca614a3"
|
||||
integrity sha512-DE4UNaBXwtVoDJ0ccBdLVjFTWL70NRuWNCxEieTI3lrq9ORB9aOCQEKstwDXBl87NvFdbqh/p7eINGyj0BthJA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.28.3"
|
||||
"@babel/plugin-transform-react-jsx-self" "^7.27.1"
|
||||
"@babel/plugin-transform-react-jsx-source" "^7.27.1"
|
||||
"@rolldown/pluginutils" "1.0.0-beta.34"
|
||||
"@rolldown/pluginutils" "1.0.0-beta.32"
|
||||
"@types/babel__core" "^7.20.5"
|
||||
react-refresh "^0.17.0"
|
||||
|
||||
"@webgpu/glslang@^0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@webgpu/glslang/-/glslang-0.0.15.tgz#f5ccaf6015241e6175f4b90906b053f88483d1f2"
|
||||
integrity sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==
|
||||
|
||||
"@xmldom/xmldom@^0.8.8":
|
||||
version "0.8.11"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608"
|
||||
@ -3034,7 +3112,7 @@ antd-style@^3.7.1:
|
||||
"@emotion/utils" "^1.2.1"
|
||||
use-merge-value "^1.2.0"
|
||||
|
||||
antd@^5.27.1:
|
||||
antd@^5.27.0:
|
||||
version "5.27.1"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-5.27.1.tgz#5378fc017cb4057ffefe2a670f20e54b924d897d"
|
||||
integrity sha512-jGMSdBN7hAMvPV27B4RhzZfL6n6yu8yDbo7oXrlJasaOqB7bSDPcjdEy1kXy3JPsny/Qazb1ykzRI4EfcByAPQ==
|
||||
@ -3437,6 +3515,14 @@ buffer@^5.1.0, buffer@^5.5.0:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
buffer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
builder-util-runtime@9.3.1:
|
||||
version "9.3.1"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz#0daedde0f6d381f2a00a50a407b166fe7dca1a67"
|
||||
@ -3590,14 +3676,6 @@ chalk-template@0.4.0:
|
||||
dependencies:
|
||||
chalk "^4.1.2"
|
||||
|
||||
chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6"
|
||||
@ -3614,6 +3692,14 @@ chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^5.0.1:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.0.tgz#a1a8d294ea3526dbb77660f12649a08490e33ab8"
|
||||
@ -3639,6 +3725,18 @@ character-reference-invalid@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
|
||||
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
|
||||
|
||||
chevrotain@^10.1.2:
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-10.5.0.tgz#9c1dc62ef0753bb562dbe521b5f72d041bad624e"
|
||||
integrity sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==
|
||||
dependencies:
|
||||
"@chevrotain/cst-dts-gen" "10.5.0"
|
||||
"@chevrotain/gast" "10.5.0"
|
||||
"@chevrotain/types" "10.5.0"
|
||||
"@chevrotain/utils" "10.5.0"
|
||||
lodash "4.17.21"
|
||||
regexp-to-ast "0.5.0"
|
||||
|
||||
chownr@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
@ -3824,17 +3922,18 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
concurrently@^9.2.1:
|
||||
version "9.2.1"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.1.tgz#248ea21b95754947be2dad9c3e4b60f18ca4e44f"
|
||||
integrity sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==
|
||||
concurrently@^9.2.0:
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.0.tgz#233e3892ceb0b5db9fd49e9c8c739737a7b638b5"
|
||||
integrity sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==
|
||||
dependencies:
|
||||
chalk "4.1.2"
|
||||
rxjs "7.8.2"
|
||||
shell-quote "1.8.3"
|
||||
supports-color "8.1.1"
|
||||
tree-kill "1.2.2"
|
||||
yargs "17.7.2"
|
||||
chalk "^4.1.2"
|
||||
lodash "^4.17.21"
|
||||
rxjs "^7.8.1"
|
||||
shell-quote "^1.8.1"
|
||||
supports-color "^8.1.1"
|
||||
tree-kill "^1.2.2"
|
||||
yargs "^17.7.2"
|
||||
|
||||
config-file-ts@0.2.8-rc1:
|
||||
version "0.2.8-rc1"
|
||||
@ -3919,7 +4018,7 @@ cosmiconfig@^8.1.3:
|
||||
parse-json "^5.2.0"
|
||||
path-type "^4.0.0"
|
||||
|
||||
country-list@^2.4.1:
|
||||
country-list@^2.3.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/country-list/-/country-list-2.4.1.tgz#468b60a91004533133352a629de07a3a9d459899"
|
||||
integrity sha512-KhVV/UfUV3dSNpsWIqHTQxLpYDKPKz1UwkRjadt+YbX2PRhyCEihEoS5XgB7J7AMXpkicvl+tRHvkNI5wbji/g==
|
||||
@ -4234,16 +4333,11 @@ data-view-byte-offset@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
dayjs@^1.11.11:
|
||||
dayjs@^1.11.11, dayjs@^1.11.13:
|
||||
version "1.11.13"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
|
||||
dayjs@^1.11.18:
|
||||
version "1.11.18"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11"
|
||||
integrity sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
@ -4483,6 +4577,11 @@ dotenv@^17.2.1:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32"
|
||||
integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==
|
||||
|
||||
draco3d@^1.4.1:
|
||||
version "1.5.7"
|
||||
resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.7.tgz#94f9bce293eb8920c159dc91a4ce9124a9e899e0"
|
||||
integrity sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==
|
||||
|
||||
dunder-proto@^1.0.0, dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
@ -4569,10 +4668,10 @@ electron-to-chromium@^1.5.204:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz#609c29502fd7257b4d721e3446f3ae391a0ca1b3"
|
||||
integrity sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==
|
||||
|
||||
electron@^37.4.0:
|
||||
version "37.4.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-37.4.0.tgz#08b2eff9d6250fac7b298f17f93946ecd586f38e"
|
||||
integrity sha512-HhsSdWowE5ODOeWNc/323Ug2C52mq/TqNBG+4uMeOA3G2dMXNc/nfyi0RYu1rJEgiaJLEjtHveeZZaYRYFsFCQ==
|
||||
electron@^37.2.6:
|
||||
version "37.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-37.3.1.tgz#92d0299593c4302dcdf3305af917c60dad69719f"
|
||||
integrity sha512-7DhktRLqhe6OJh/Bo75bTI0puUYEmIwSzMinocgO63mx3MVjtIn2tYMzLmAleNIlud2htkjpsMG2zT4PiTCloA==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^22.7.7"
|
||||
@ -5051,7 +5150,7 @@ eslint@^8.41.0, eslint@^8.57.1:
|
||||
strip-ansi "^6.0.1"
|
||||
text-table "^0.2.0"
|
||||
|
||||
eslint@^9.34.0:
|
||||
eslint@^9.33.0:
|
||||
version "9.34.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.34.0.tgz#0ea1f2c1b5d1671db8f01aa6b8ce722302016f7b"
|
||||
integrity sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==
|
||||
@ -5283,6 +5382,11 @@ fecha@^4.2.1:
|
||||
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
|
||||
integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
|
||||
|
||||
fflate@^0.6.9:
|
||||
version "0.6.10"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43"
|
||||
integrity sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
@ -5931,7 +6035,7 @@ iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
ieee754@^1.1.13:
|
||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
@ -6327,6 +6431,13 @@ iterator.prototype@^1.1.4:
|
||||
has-symbols "^1.1.0"
|
||||
set-function-name "^2.0.2"
|
||||
|
||||
its-fine@^1.0.6:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-1.2.5.tgz#5466c287f86a0a73e772c8d8d515626c97195dc9"
|
||||
integrity sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==
|
||||
dependencies:
|
||||
"@types/react-reconciler" "^0.28.0"
|
||||
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
||||
@ -6459,6 +6570,11 @@ keyv@^4.0.0, keyv@^4.5.3, keyv@^4.5.4:
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
ktx-parse@^0.4.5:
|
||||
version "0.4.5"
|
||||
resolved "https://registry.yarnpkg.com/ktx-parse/-/ktx-parse-0.4.5.tgz#79905e22281a9d3e602b2ff522df1ee7d1813aa6"
|
||||
integrity sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg==
|
||||
|
||||
lazy-val@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.5.tgz#6cf3b9f5bc31cee7ee3e369c0832b7583dcd923d"
|
||||
@ -6536,7 +6652,7 @@ lodash.merge@^4.6.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash@^4.17.15, lodash@^4.17.21:
|
||||
lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@ -6567,7 +6683,7 @@ longest-streak@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
|
||||
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
|
||||
|
||||
loose-envify@^1.4.0:
|
||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
@ -7348,6 +7464,11 @@ ml-matrix@^6.10.4:
|
||||
is-any-array "^2.0.1"
|
||||
ml-array-rescale "^1.3.7"
|
||||
|
||||
mmd-parser@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mmd-parser/-/mmd-parser-1.0.4.tgz#87cc05782cb5974ca854f0303fc5147bc9d690e7"
|
||||
integrity sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==
|
||||
|
||||
moment@^2.30.1:
|
||||
version "2.30.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
|
||||
@ -7548,6 +7669,14 @@ onetime@^5.1.0, onetime@^5.1.2:
|
||||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
opentype.js@^1.3.3:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-1.3.4.tgz#1c0e72e46288473cc4a4c6a2dc60fd7fe6020d77"
|
||||
integrity sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==
|
||||
dependencies:
|
||||
string.prototype.codepointat "^0.2.1"
|
||||
tiny-inflate "^1.0.3"
|
||||
|
||||
optionator@^0.9.3:
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
|
||||
@ -7855,6 +7984,11 @@ postcss@^8.5.6:
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
potpack@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
|
||||
integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@ -8384,12 +8518,13 @@ react-country-flag@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/react-country-flag/-/react-country-flag-3.1.0.tgz#f0c4c332934a77d3e894ba4800634f7a887e53d4"
|
||||
integrity sha512-JWQFw1efdv9sTC+TGQvTKXQg1NKbDU2mBiAiRWcKM9F1sK+/zjhP2yGmm8YDddWyZdXVkR8Md47rPMJmo4YO5g==
|
||||
|
||||
react-dom@^19.1.1:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893"
|
||||
integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==
|
||||
react-dom@^18.2.0:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
|
||||
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
|
||||
dependencies:
|
||||
scheduler "^0.26.0"
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.2"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
@ -8418,6 +8553,14 @@ react-markdown@^10.1.0:
|
||||
unist-util-visit "^5.0.0"
|
||||
vfile "^6.0.0"
|
||||
|
||||
react-reconciler@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b"
|
||||
integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.21.0"
|
||||
|
||||
react-refresh@^0.17.0:
|
||||
version "0.17.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.17.0.tgz#b7e579c3657f23d04eccbe4ad2e58a8ed51e7e53"
|
||||
@ -8433,7 +8576,7 @@ react-responsive@^10.0.1:
|
||||
prop-types "^15.6.1"
|
||||
shallow-equal "^3.1.0"
|
||||
|
||||
react-router-dom@^7.8.2:
|
||||
react-router-dom@^7.8.0:
|
||||
version "7.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.8.2.tgz#25a8fc36588189baf3bbb5e360c8ffffbd2beabc"
|
||||
integrity sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==
|
||||
@ -8448,10 +8591,25 @@ react-router@7.8.2:
|
||||
cookie "^1.0.1"
|
||||
set-cookie-parser "^2.6.0"
|
||||
|
||||
react@^19.1.1:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af"
|
||||
integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==
|
||||
react-stl-viewer@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-stl-viewer/-/react-stl-viewer-2.5.0.tgz#c6478b5926b8a3cd2bd366101483818d891f04c9"
|
||||
integrity sha512-jlYId05N0P9rKVEdOfLO1bPsS9SYfMRxjnKvhks6T/c4HU0BxnNcZpRr4gfpJU0nFL6HHgmVKhKTh1LCHNcZuA==
|
||||
dependencies:
|
||||
"@react-three/fiber" "^8.15.5"
|
||||
three-stdlib "2.17.2"
|
||||
|
||||
react-use-measure@^2.1.7:
|
||||
version "2.1.7"
|
||||
resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.7.tgz#36b8a2e7fd2fa58109ab851b3addcb0aad66ad1d"
|
||||
integrity sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==
|
||||
|
||||
react@^18.2.0:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
||||
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
read-binary-file-arch@^1.0.6:
|
||||
version "1.0.6"
|
||||
@ -8500,6 +8658,11 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
|
||||
get-proto "^1.0.1"
|
||||
which-builtin-type "^1.2.1"
|
||||
|
||||
regexp-to-ast@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24"
|
||||
integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==
|
||||
|
||||
regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19"
|
||||
@ -8732,7 +8895,7 @@ rw@1:
|
||||
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
|
||||
integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
|
||||
|
||||
rxjs@7.8.2:
|
||||
rxjs@^7.8.1:
|
||||
version "7.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b"
|
||||
integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==
|
||||
@ -8794,10 +8957,19 @@ sax@^1.2.4, sax@^1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f"
|
||||
integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==
|
||||
|
||||
scheduler@^0.26.0:
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337"
|
||||
integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==
|
||||
scheduler@^0.21.0:
|
||||
version "0.21.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"
|
||||
integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
scheduler@^0.23.2:
|
||||
version "0.23.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
|
||||
integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
scroll-into-view-if-needed@^3.1.0:
|
||||
version "3.1.0"
|
||||
@ -8953,7 +9125,7 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
shell-quote@1.8.3:
|
||||
shell-quote@^1.8.1:
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b"
|
||||
integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==
|
||||
@ -9230,6 +9402,11 @@ string-width@^5.0.1, string-width@^5.1.2:
|
||||
emoji-regex "^9.2.2"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
string.prototype.codepointat@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
|
||||
integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
|
||||
|
||||
string.prototype.matchall@^4.0.12:
|
||||
version "4.0.12"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0"
|
||||
@ -9415,13 +9592,6 @@ sumchecker@^3.0.1:
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
|
||||
supports-color@8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
||||
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
@ -9434,11 +9604,23 @@ supports-color@^7.1.0:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-color@^8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
||||
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
suspend-react@^0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.1.3.tgz#a52f49d21cfae9a2fb70bd0c68413d3f9d90768e"
|
||||
integrity sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==
|
||||
|
||||
svg-parser@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
|
||||
@ -9514,6 +9696,23 @@ text-table@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
|
||||
three-stdlib@2.17.2:
|
||||
version "2.17.2"
|
||||
resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.17.2.tgz#9c2a2914a16123853531612dac8870e6e242685b"
|
||||
integrity sha512-7ZLCJJogtn1D1MlUi7q0iLUbrxj7K++YxjHIIz5AZ4wX4E137BgiiTmhH4XhAuvXGRk9ph3ZtoHTfJBXhqDX3w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.16.7"
|
||||
"@types/offscreencanvas" "^2019.6.4"
|
||||
"@webgpu/glslang" "^0.0.15"
|
||||
chevrotain "^10.1.2"
|
||||
draco3d "^1.4.1"
|
||||
fflate "^0.6.9"
|
||||
ktx-parse "^0.4.5"
|
||||
mmd-parser "^1.0.4"
|
||||
opentype.js "^1.3.3"
|
||||
potpack "^1.0.1"
|
||||
zstddec "^0.0.2"
|
||||
|
||||
three@^0.159.0:
|
||||
version "0.159.0"
|
||||
resolved "https://registry.yarnpkg.com/three/-/three-0.159.0.tgz#6576b1210805b14f0765bac41fd0e4ec18e43b2e"
|
||||
@ -9536,6 +9735,11 @@ tiny-async-pool@1.3.0:
|
||||
dependencies:
|
||||
semver "^5.5.0"
|
||||
|
||||
tiny-inflate@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
|
||||
integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
|
||||
|
||||
tinyglobby@^0.2.14:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d"
|
||||
@ -9573,7 +9777,7 @@ toidentifier@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
tree-kill@1.2.2:
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
@ -9929,12 +10133,12 @@ vite-plugin-svgo@^2.0.0:
|
||||
dependencies:
|
||||
svgo "3.3.2"
|
||||
|
||||
vite-plugin-svgr@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.5.0.tgz#253e4c703d1f0b30935c285ca8621f4857952338"
|
||||
integrity sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==
|
||||
vite-plugin-svgr@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz#742f16f11375996306c696ec323e4d23f6005075"
|
||||
integrity sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^5.2.0"
|
||||
"@rollup/pluginutils" "^5.1.3"
|
||||
"@svgr/core" "^8.1.0"
|
||||
"@svgr/plugin-jsx" "^8.1.0"
|
||||
|
||||
@ -10131,7 +10335,7 @@ yargs-parser@^21.1.1:
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@17.7.2, yargs@^17.0.1, yargs@^17.6.2:
|
||||
yargs@^17.0.1, yargs@^17.6.2, yargs@^17.7.2:
|
||||
version "17.7.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
||||
@ -10157,6 +10361,16 @@ yocto-queue@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zstddec@^0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4"
|
||||
integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==
|
||||
|
||||
zustand@^3.7.1:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d"
|
||||
integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==
|
||||
|
||||
zwitch@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user