Compare commits

...

9 Commits

8 changed files with 198 additions and 58 deletions

View File

@ -80,12 +80,20 @@
font-size: 95%; font-size: 95%;
} }
.object-display-tag { .ant-select-selection-item .country-display {
margin-left: 1px !important;
}
.object-display-tag,
.object-type-display-tag,
.country-display {
opacity: 1; opacity: 1;
transition: opacity 0.15s ease-in-out; transition: opacity 0.15s ease-in-out;
} }
.ant-select-focused .object-display-tag { .object-select.ant-select-focused .object-display-tag,
.ant-select-focused .object-type-display-tag,
.ant-select-focused .country-display {
opacity: 0.5; opacity: 0.5;
} }
@ -379,7 +387,7 @@ body {
margin-right: 1px !important; margin-right: 1px !important;
} }
.ant-select-selection-item .object-display-tag { .object-select .ant-select-selection-item .object-display-tag {
top: 1.5px; top: 1.5px;
} }
@ -621,11 +629,20 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
right 0.15s ease-in-out; right 0.15s ease-in-out;
} }
.sidebar-scroll .simplebar-track.simplebar-vertical {
border-radius: 0 0 6.5px 0;
}
.sidebar-scroll.dark .simplebar-track.simplebar-vertical { .sidebar-scroll.dark .simplebar-track.simplebar-vertical {
background-color: #141414; background-color: #141414;
border-inline-end: 1px solid rgba(253, 253, 253, 0.12); border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
border-bottom: 1px solid rgba(253, 253, 253, 0.12); border-bottom: 1px solid rgba(253, 253, 253, 0.12);
border-radius: 0 0 6.5px 0; }
.sidebar-scroll.light .simplebar-track.simplebar-vertical {
background-color: #ffffff;
border-inline-end: 1px solid rgba(5, 5, 5, 0.06);
border-bottom: 1px solid rgba(5, 5, 5, 0.06);
} }
.sidebar-scroll.simplebar-scrolling .simplebar-track.simplebar-vertical { .sidebar-scroll.simplebar-scrolling .simplebar-track.simplebar-vertical {

View File

@ -57,7 +57,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
disabled={downloading} disabled={downloading}
sideBarGrow={true} sideBarGrow={true}
sideBar={ sideBar={
<div style={{ minHeight: '500px', flexGrow: 1 }}> <div style={{ minHeight: '760px', flexGrow: 1 }}>
<TemplatePreview <TemplatePreview
objectData={objectData?.object} objectData={objectData?.object}
documentTemplate={objectData?.documentTemplate} documentTemplate={objectData?.documentTemplate}

View File

@ -13,7 +13,7 @@ const CountryDisplay = ({ countryCode }) => {
} }
return ( return (
<Flex gap={'small'} align='center'> <Flex gap={'small'} align='center' className='country-display'>
<Flag <Flag
code={country.code} code={country.code}
size='middle' size='middle'

View File

@ -28,7 +28,7 @@ const CountrySelect = ({
value: country.code, value: country.code,
name: country.name, name: country.name,
label: ( label: (
<Flex gap='small' align='center'> <Flex gap='small' align='center' className='country-display'>
<Flag code={country.code} size='small' borderRadius={3} /> <Flag code={country.code} size='small' borderRadius={3} />
<Text elipsis>{country.name}</Text> <Text elipsis>{country.name}</Text>
</Flex> </Flex>

View File

@ -1,5 +1,12 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useState, useEffect, useContext, useCallback, useMemo, useRef } from 'react' import {
useState,
useEffect,
useContext,
useCallback,
useMemo,
useRef
} from 'react'
// import { getModelByName } from '../../../database/ObjectModels' // import { getModelByName } from '../../../database/ObjectModels'
import DocumentPrinterIcon from '../../Icons/DocumentPrinterIcon' import DocumentPrinterIcon from '../../Icons/DocumentPrinterIcon'
import { Button, Dropdown, Modal } from 'antd' import { Button, Dropdown, Modal } from 'antd'

View File

@ -60,6 +60,7 @@ const ObjectSelect = ({
const [objectList, setObjectList] = useState([]) const [objectList, setObjectList] = useState([])
const [treeSelectValue, setTreeSelectValue] = useState(null) const [treeSelectValue, setTreeSelectValue] = useState(null)
const [initialLoading, setInitialLoading] = useState(true) const [initialLoading, setInitialLoading] = useState(true)
const [delayedInitialLoading, setDelayedInitalLoading] = 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 [isSearching, setIsSearching] = useState(false)
@ -358,7 +359,7 @@ const ObjectSelect = ({
const onTreeSelectChange = useCallback( const onTreeSelectChange = useCallback(
(value) => { (value) => {
// Mark this as an internal change // Mark this as an internal change
setIsSearching(false) if (!multiple) 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)
@ -367,7 +368,7 @@ const ObjectSelect = ({
let selectedObjects = [] let selectedObjects = []
if (Array.isArray(value)) { if (Array.isArray(value)) {
selectedObjects = value selectedObjects = value
.map((id) => objectList.find((obj) => obj._id === id)) .map((id) => objectList.find((obj) => areValuesEqual(obj._id, id)))
.filter(Boolean) .filter(Boolean)
} }
setTreeSelectValue(value) setTreeSelectValue(value)
@ -399,14 +400,32 @@ const ObjectSelect = ({
if (requestId !== searchRequestIdRef.current) return if (requestId !== searchRequestIdRef.current) return
if (!Array.isArray(data)) return if (!Array.isArray(data)) return
setTreeData(buildTreeData(data, properties.length)) const searchData =
multiple && Array.isArray(treeSelectValue)
? [
...data,
...objectList.filter((object) =>
treeSelectValue.some((id) => areValuesEqual(object._id, id))
)
].filter(
(object, index, objects) =>
objects.findIndex((item) =>
areValuesEqual(item._id, object._id)
) === index
)
: data
setTreeData(buildTreeData(searchData, properties.length))
}, },
[ [
searchObjects, searchObjects,
type, type,
buildTreeData, buildTreeData,
objectPropertiesTree, objectPropertiesTree,
properties.length properties.length,
multiple,
treeSelectValue,
objectList
] ]
) )
@ -421,12 +440,29 @@ const ObjectSelect = ({
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
onTreeSelectChange( onTreeSelectChange(
multiple ? [firstLeaf.value] : firstLeaf.value multiple
? [
...(Array.isArray(treeSelectValue) ? treeSelectValue : []),
firstLeaf.value
].filter(
(item, index, values) =>
values.findIndex((value) => areValuesEqual(value, item)) ===
index
)
: firstLeaf.value
) )
setSearchValue('') setSearchValue('')
onSearch('') onSearch('')
}, },
[treeSelectProps, isSearching, treeData, multiple, onTreeSelectChange, onSearch] [
treeSelectProps,
isSearching,
treeData,
treeSelectValue,
multiple,
onTreeSelectChange,
onSearch
]
) )
// Update treeData when objectPropertiesTree changes // Update treeData when objectPropertiesTree changes
useEffect(() => { useEffect(() => {
@ -444,6 +480,60 @@ const ObjectSelect = ({
useEffect(() => { useEffect(() => {
const handleValue = async () => { const handleValue = async () => {
if (
multiple &&
Array.isArray(value) &&
getValueIdentity(valueRef.current) !== getValueIdentity(value) &&
type != 'unknown'
) {
valueRef.current = value
const fullValues = await Promise.all(value.map(fetchFullObjectIfNeeded))
const pathKeys = []
if (fullValues.length === 0) {
handleFetchObjectsProperties()
} else {
fullValues.forEach((fullValue) => {
const valueFilter = { ...filter }
const parentKeys = []
properties.forEach((prop) => {
if (!Object.prototype.hasOwnProperty.call(fullValue, prop)) return
const filterValue = fullValue[prop]
let valueString = filterValue
if (
filterValue &&
typeof filterValue === 'object' &&
filterValue._id
) {
valueString = filterValue._id
} else if (filterValue?.name) {
valueString = filterValue.name
} else if (Array.isArray(filterValue)) {
valueString = filterValue.join(',')
}
valueFilter[prop] = valueString
pathKeys.push(
parentKeys.concat(prop + ':' + valueString).join('-')
)
parentKeys.push(valueString)
})
handleFetchObjectsProperties(valueFilter)
})
}
setExpandedKeys([...new Set(pathKeys)])
setTreeSelectValue(
value
.map((item) => (item && typeof item === 'object' ? item._id : item))
.filter((id) => id != null)
)
setInitialized(true)
return
}
if ( if (
value && value &&
typeof value === 'object' && typeof value === 'object' &&
@ -527,7 +617,8 @@ const ObjectSelect = ({
fetchFullObjectIfNeeded, fetchFullObjectIfNeeded,
type, type,
connected, connected,
getValueIdentity getValueIdentity,
multiple
]) ])
const prevValuesRef = useRef({ type, masterFilter }) const prevValuesRef = useRef({ type, masterFilter })
@ -583,6 +674,16 @@ const ObjectSelect = ({
} }
}, [value, getValueIdentity, type, masterFilter]) }, [value, getValueIdentity, type, masterFilter])
useEffect(() => {
if (initialLoading == false) {
setTimeout(() => {
setDelayedInitalLoading(false)
}, 100)
} else {
setDelayedInitalLoading(true)
}
}, [initialLoading])
const placeholder = useMemo( const placeholder = useMemo(
() => () =>
type == 'unknown' || type == undefined type == 'unknown' || type == undefined
@ -609,34 +710,48 @@ const ObjectSelect = ({
) )
} }
if (initialLoading) {
return <TreeSelect disabled loading placeholder='Loading...' />
}
// --- Main TreeSelect UI --- // --- Main TreeSelect UI ---
return ( return (
<TreeSelect <div>
key={treeVersion} <TreeSelect
treeDataSimpleMode={false} key={treeVersion}
treeDefaultExpandAll={true} treeDataSimpleMode={false}
treeExpandedKeys={expandedKeys} treeDefaultExpandAll={true}
onTreeExpand={setExpandedKeys} treeExpandedKeys={expandedKeys}
treeData={treeData} onTreeExpand={setExpandedKeys}
showSearch={showSearch} treeData={treeData}
filterTreeNode={() => true} showSearch={showSearch}
multiple={multiple} filterTreeNode={() => true}
loadData={isSearching ? undefined : loadData} multiple={multiple}
showCheckedStrategy={SHOW_CHILD} loadData={isSearching ? undefined : loadData}
placeholder={placeholder} showCheckedStrategy={SHOW_CHILD}
{...treeSelectProps} placeholder={placeholder}
{...rest} {...treeSelectProps}
value={treeSelectValue} {...rest}
onChange={onTreeSelectChange} value={treeSelectValue}
onSearch={onSearch} onChange={onTreeSelectChange}
onInputKeyDown={onInputKeyDown} onSearch={onSearch}
searchValue={searchValue} onInputKeyDown={onInputKeyDown}
disabled={disabled || type == 'unknown' || type == undefined} searchValue={searchValue}
/> disabled={disabled || type == 'unknown' || type == undefined}
style={{ opacity: delayedInitialLoading ? 0 : 1 }}
className={multiple ? 'object-select-multiple' : 'object-select'}
/>
{delayedInitialLoading && (
<TreeSelect
disabled
loading
placeholder='Loading...'
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0
}}
/>
)}
</div>
) )
} }

View File

@ -28,6 +28,7 @@ const ObjectTypeDisplay = ({
color={color} color={color}
style={{ margin: 0, ...style }} style={{ margin: 0, ...style }}
icon={showIcon && Icon ? <Icon /> : undefined} icon={showIcon && Icon ? <Icon /> : undefined}
className='object-type-display-tag'
> >
{showLabel && model.label && model.label} {showLabel && model.label && model.label}
</Tag> </Tag>

View File

@ -131,6 +131,13 @@ export const Product = {
readOnly: true, readOnly: true,
columnWidth: 180 columnWidth: 180
}, },
{
name: 'updatedAt',
label: 'Updated At',
type: 'dateTime',
readOnly: true,
columnWidth: 175
},
{ {
name: 'name', name: 'name',
label: 'Name', label: 'Name',
@ -139,31 +146,15 @@ export const Product = {
columnWidth: 200, columnWidth: 200,
columnFixed: 'left' columnFixed: 'left'
}, },
{
name: 'updatedAt',
label: 'Updated At',
type: 'dateTime',
readOnly: true,
columnWidth: 175
},
{ {
name: 'productCategory', name: 'productCategory',
label: 'Product Category', label: 'Category',
required: true, required: true,
type: 'object', type: 'object',
objectType: 'productCategory', objectType: 'productCategory',
showHyperlink: true, showHyperlink: true,
columnWidth: 200 columnWidth: 200
}, },
{
name: 'vendor',
label: 'Vendor',
required: true,
type: 'object',
objectType: 'vendor',
showHyperlink: true,
columnWidth: 200
},
{ {
name: 'version', name: 'version',
label: 'Version', label: 'Version',
@ -171,6 +162,15 @@ export const Product = {
type: 'text', type: 'text',
columnWidth: 120 columnWidth: 120
}, },
{
name: 'vendor',
label: 'Vendor',
required: true,
type: 'object',
objectType: 'vendor',
showHyperlink: true,
columnWidth: 200
},
{ {
name: 'tags', name: 'tags',
label: 'Tags', label: 'Tags',