Compare commits
No commits in common. "62805b0d6a2bd6b5a9daadacb5d9192fc9b55520" and "a896cdf223b111228d3190c1bc031db710b723e5" have entirely different histories.
62805b0d6a
...
a896cdf223
@ -196,7 +196,6 @@ code {
|
|||||||
/* --- Start of src/index.css --- */
|
/* --- Start of src/index.css --- */
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-mask {
|
.ant-modal-mask {
|
||||||
@ -603,32 +602,3 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-scroll .simplebar-track.simplebar-vertical {
|
|
||||||
right: -1px;
|
|
||||||
width: 0px !important;
|
|
||||||
transition:
|
|
||||||
width 0.15s ease-in-out,
|
|
||||||
right 0.15s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-scroll.dark .simplebar-track.simplebar-vertical {
|
|
||||||
background-color: #141414;
|
|
||||||
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
|
||||||
border-bottom: 1px solid rgba(253, 253, 253, 0.12);
|
|
||||||
border-radius: 0 0 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-scroll.simplebar-scrolling .simplebar-track.simplebar-vertical {
|
|
||||||
width: 10px !important;
|
|
||||||
right: -11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-scroll
|
|
||||||
.simplebar-track.simplebar-vertical
|
|
||||||
.simplebar-scrollbar:before {
|
|
||||||
top: 3px;
|
|
||||||
bottom: 3px;
|
|
||||||
left: 3px;
|
|
||||||
right: 3px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
|
import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
|
||||||
import SimpleBar from 'simplebar-react'
|
|
||||||
import { useThemeContext } from '../context/ThemeContext'
|
|
||||||
const { Sider } = Layout
|
const { Sider } = Layout
|
||||||
|
|
||||||
const DashboardSidebar = ({
|
const DashboardSidebar = ({
|
||||||
@ -26,7 +24,7 @@ const DashboardSidebar = ({
|
|||||||
})
|
})
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { isDarkMode } = useThemeContext()
|
|
||||||
const { isElectron } = useContext(ElectronContext)
|
const { isElectron } = useContext(ElectronContext)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -92,14 +90,11 @@ const DashboardSidebar = ({
|
|||||||
className={isElectron ? 'electron-sider' : ''}
|
className={isElectron ? 'electron-sider' : ''}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
style={{ height: '100%', minHeight: 0 }}
|
style={{ height: '100%' }}
|
||||||
vertical
|
vertical
|
||||||
className='ant-menu-root ant-menu-inline ant-menu-light'
|
className='ant-menu-root ant-menu-inline ant-menu-light'
|
||||||
>
|
>
|
||||||
<SimpleBar
|
<div style={{ maxHeight: '100%', overflowY: 'auto', height: '100%' }}>
|
||||||
style={{ flexGrow: 1, minHeight: 0 }}
|
|
||||||
className={`sidebar-scroll ${isDarkMode ? 'dark' : 'light'}`}
|
|
||||||
>
|
|
||||||
<Menu
|
<Menu
|
||||||
mode='inline'
|
mode='inline'
|
||||||
selectedKeys={[selectedKey]}
|
selectedKeys={[selectedKey]}
|
||||||
@ -108,7 +103,7 @@ const DashboardSidebar = ({
|
|||||||
style={{ flexGrow: 1, border: 'none' }}
|
style={{ flexGrow: 1, border: 'none' }}
|
||||||
_internalDisableMenuItemTitleTooltip
|
_internalDisableMenuItemTitleTooltip
|
||||||
/>
|
/>
|
||||||
</SimpleBar>
|
</div>
|
||||||
<Flex style={{ padding: '4px', width: '100%' }}>
|
<Flex style={{ padding: '4px', width: '100%' }}>
|
||||||
<Button
|
<Button
|
||||||
size={isElectron ? 'middle' : 'large'}
|
size={isElectron ? 'middle' : 'large'}
|
||||||
|
|||||||
@ -26,19 +26,9 @@ const areValuesEqual = (v1, v2) => {
|
|||||||
return String(id1) === String(id2)
|
return String(id1) === String(id2)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFirstSelectableLeaf = (nodes) => {
|
|
||||||
if (!Array.isArray(nodes)) return null
|
|
||||||
for (const node of nodes) {
|
|
||||||
if (node.isLeaf && node.selectable !== false) return node
|
|
||||||
const found = getFirstSelectableLeaf(node.children)
|
|
||||||
if (found) return found
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const ObjectSelect = ({
|
const ObjectSelect = ({
|
||||||
type = 'unknown',
|
type = 'unknown',
|
||||||
showSearch = true,
|
showSearch = false,
|
||||||
multiple = false,
|
multiple = false,
|
||||||
treeSelectProps = EMPTY_OBJECT,
|
treeSelectProps = EMPTY_OBJECT,
|
||||||
filter = EMPTY_OBJECT,
|
filter = EMPTY_OBJECT,
|
||||||
@ -48,7 +38,7 @@ const ObjectSelect = ({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const { fetchObjectsByProperty, fetchObject, connected, searchObjects } =
|
const { fetchObjectsByProperty, fetchObject, connected } =
|
||||||
useContext(ApiServerContext)
|
useContext(ApiServerContext)
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
// --- State ---
|
// --- State ---
|
||||||
@ -62,9 +52,6 @@ const ObjectSelect = ({
|
|||||||
const [initialLoading, setInitialLoading] = useState(true)
|
const [initialLoading, setInitialLoading] = useState(true)
|
||||||
const [expandedKeys, setExpandedKeys] = useState([])
|
const [expandedKeys, setExpandedKeys] = useState([])
|
||||||
const [treeVersion, setTreeVersion] = useState(0)
|
const [treeVersion, setTreeVersion] = useState(0)
|
||||||
const [isSearching, setIsSearching] = useState(false)
|
|
||||||
const [searchValue, setSearchValue] = useState('')
|
|
||||||
const searchRequestIdRef = useRef(0)
|
|
||||||
const valueRef = useRef(null)
|
const valueRef = useRef(null)
|
||||||
|
|
||||||
// Refs to track value changes
|
// Refs to track value changes
|
||||||
@ -358,7 +345,6 @@ const ObjectSelect = ({
|
|||||||
const onTreeSelectChange = useCallback(
|
const onTreeSelectChange = useCallback(
|
||||||
(value) => {
|
(value) => {
|
||||||
// Mark this as an internal change
|
// Mark this as an internal change
|
||||||
setIsSearching(false)
|
|
||||||
isInternalChangeRef.current = true
|
isInternalChangeRef.current = true
|
||||||
|
|
||||||
// value can be a string (single) or array (multiple)
|
// value can be a string (single) or array (multiple)
|
||||||
@ -382,55 +368,8 @@ const ObjectSelect = ({
|
|||||||
[multiple, objectList, onChange]
|
[multiple, objectList, onChange]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onSearch = useCallback(
|
|
||||||
async (search) => {
|
|
||||||
setSearchValue(search)
|
|
||||||
const trimmed = search?.trim() ?? ''
|
|
||||||
if (!trimmed) {
|
|
||||||
searchRequestIdRef.current += 1
|
|
||||||
setIsSearching(false)
|
|
||||||
setTreeData(buildTreeData(objectPropertiesTree))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestId = ++searchRequestIdRef.current
|
|
||||||
setIsSearching(true)
|
|
||||||
const data = await searchObjects(type, trimmed)
|
|
||||||
if (requestId !== searchRequestIdRef.current) return
|
|
||||||
if (!Array.isArray(data)) return
|
|
||||||
|
|
||||||
setTreeData(buildTreeData(data, properties.length))
|
|
||||||
},
|
|
||||||
[
|
|
||||||
searchObjects,
|
|
||||||
type,
|
|
||||||
buildTreeData,
|
|
||||||
objectPropertiesTree,
|
|
||||||
properties.length
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onInputKeyDown = useCallback(
|
|
||||||
(e) => {
|
|
||||||
treeSelectProps.onInputKeyDown?.(e)
|
|
||||||
if (e.defaultPrevented || e.key !== 'Enter' || !isSearching) return
|
|
||||||
|
|
||||||
const firstLeaf = getFirstSelectableLeaf(treeData)
|
|
||||||
if (!firstLeaf?.value) return
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
onTreeSelectChange(
|
|
||||||
multiple ? [firstLeaf.value] : firstLeaf.value
|
|
||||||
)
|
|
||||||
setSearchValue('')
|
|
||||||
onSearch('')
|
|
||||||
},
|
|
||||||
[treeSelectProps, isSearching, treeData, multiple, onTreeSelectChange, onSearch]
|
|
||||||
)
|
|
||||||
// Update treeData when objectPropertiesTree changes
|
// Update treeData when objectPropertiesTree changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSearching) return
|
|
||||||
if (objectPropertiesTree && Object.keys(objectPropertiesTree).length > 0) {
|
if (objectPropertiesTree && Object.keys(objectPropertiesTree).length > 0) {
|
||||||
const newTreeData = buildTreeData(objectPropertiesTree)
|
const newTreeData = buildTreeData(objectPropertiesTree)
|
||||||
setTreeData((prev) => {
|
setTreeData((prev) => {
|
||||||
@ -440,7 +379,7 @@ const ObjectSelect = ({
|
|||||||
return prev
|
return prev
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [objectPropertiesTree, properties, buildTreeData, isSearching])
|
}, [objectPropertiesTree, properties, buildTreeData])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleValue = async () => {
|
const handleValue = async () => {
|
||||||
@ -541,9 +480,6 @@ const ObjectSelect = ({
|
|||||||
JSON.stringify(prevValues.masterFilter) !== JSON.stringify(masterFilter)
|
JSON.stringify(prevValues.masterFilter) !== JSON.stringify(masterFilter)
|
||||||
|
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
searchRequestIdRef.current += 1
|
|
||||||
setIsSearching(false)
|
|
||||||
setSearchValue('')
|
|
||||||
setObjectPropertiesTree({})
|
setObjectPropertiesTree({})
|
||||||
setObjectList([])
|
setObjectList([])
|
||||||
setTreeData([])
|
setTreeData([])
|
||||||
@ -623,18 +559,14 @@ const ObjectSelect = ({
|
|||||||
onTreeExpand={setExpandedKeys}
|
onTreeExpand={setExpandedKeys}
|
||||||
treeData={treeData}
|
treeData={treeData}
|
||||||
showSearch={showSearch}
|
showSearch={showSearch}
|
||||||
filterTreeNode={() => true}
|
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
loadData={isSearching ? undefined : loadData}
|
loadData={loadData}
|
||||||
showCheckedStrategy={SHOW_CHILD}
|
showCheckedStrategy={SHOW_CHILD}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
{...treeSelectProps}
|
{...treeSelectProps}
|
||||||
{...rest}
|
{...rest}
|
||||||
value={treeSelectValue}
|
value={treeSelectValue}
|
||||||
onChange={onTreeSelectChange}
|
onChange={onTreeSelectChange}
|
||||||
onSearch={onSearch}
|
|
||||||
onInputKeyDown={onInputKeyDown}
|
|
||||||
searchValue={searchValue}
|
|
||||||
disabled={disabled || type == 'unknown' || type == undefined}
|
disabled={disabled || type == 'unknown' || type == undefined}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -744,23 +744,20 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
[offLockEvent]
|
[offLockEvent]
|
||||||
)
|
)
|
||||||
|
|
||||||
const showError = useCallback(
|
const showError = useCallback((error, callback = null) => {
|
||||||
(error, callback = null) => {
|
const code = error.response.data.code || 'UNKNOWN'
|
||||||
const code = error.response.data.code || 'UNKNOWN'
|
if (code == 'UNAUTHORIZED') {
|
||||||
if (code == 'UNAUTHORIZED') {
|
setUnauthenticated()
|
||||||
setUnauthenticated()
|
return
|
||||||
return
|
}
|
||||||
}
|
var content = `Error ${error.code} (${error.status}): ${error.message}`
|
||||||
var content = `Error ${error.code} (${error.status}): ${error.message}`
|
if (error.response?.data?.error) {
|
||||||
if (error.response?.data?.error) {
|
content = `${error.response?.data?.error} (${error.status})`
|
||||||
content = `${error.response?.data?.error} (${error.status})`
|
}
|
||||||
}
|
setErrorModalContent(content)
|
||||||
setErrorModalContent(content)
|
setRetryCallback(() => callback)
|
||||||
setRetryCallback(() => callback)
|
setShowErrorModal(true)
|
||||||
setShowErrorModal(true)
|
}, [setUnauthenticated])
|
||||||
},
|
|
||||||
[setUnauthenticated]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleRetry = () => {
|
const handleRetry = () => {
|
||||||
setShowErrorModal(false)
|
setShowErrorModal(false)
|
||||||
@ -914,27 +911,6 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchObjects = async (type, search) => {
|
|
||||||
const searchUrl = `${config.backendUrl}/${getObjectEndpoint(type)}/search`
|
|
||||||
logger.debug('Searching objects for ' + type, { search })
|
|
||||||
try {
|
|
||||||
const response = await axios.get(searchUrl, {
|
|
||||||
params: { search },
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
showError(err, () => {
|
|
||||||
searchObjects(type, search)
|
|
||||||
})
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update filament information
|
// Update filament information
|
||||||
const updateObject = async (id, type, value) => {
|
const updateObject = async (id, type, value) => {
|
||||||
const updateUrl = `${config.backendUrl}/${getObjectEndpoint(type)}/${id}`
|
const updateUrl = `${config.backendUrl}/${getObjectEndpoint(type)}/${id}`
|
||||||
@ -1848,7 +1824,6 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
fetchObject,
|
fetchObject,
|
||||||
fetchObjects,
|
fetchObjects,
|
||||||
fetchObjectsByProperty,
|
fetchObjectsByProperty,
|
||||||
searchObjects,
|
|
||||||
fetchSpotlightData,
|
fetchSpotlightData,
|
||||||
getModelStats,
|
getModelStats,
|
||||||
getModelHistory,
|
getModelHistory,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user