sozsoft-platform/ui/src/views/list/Tree.tsx
2026-06-07 22:42:02 +03:00

1672 lines
62 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Container from '@/components/shared/Container'
import { Dialog, Notification, toast } from '@/components/ui'
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
import { executeEditorScript } from '@/utils/editorScriptRuntime'
import {
DbTypeEnum,
EditingFormItemDto,
GridDto,
ListFormCustomizationTypeEnum,
PlatformEditorTypes,
SubFormTabTypeEnum,
} from '@/proxy/form/models'
import {
getListFormCustomization,
postListFormCustomization,
} from '@/services/list-form-customization.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import useResponsive from '@/utils/hooks/useResponsive'
import { captionize } from 'devextreme/core/utils/inflector'
import { Template } from 'devextreme-react/core/template'
import TreeListDx, {
ColumnChooser,
ColumnFixing,
Editing,
FilterPanel,
FilterRow,
HeaderFilter,
LoadPanel,
Pager,
Paging,
RemoteOperations,
Scrolling,
SearchPanel,
Selection,
Sorting,
Toolbar,
TreeListRef,
TreeListTypes,
} from 'devextreme-react/tree-list'
import { Item } from 'devextreme-react/toolbar'
import CustomStore from 'devextreme/data/custom_store'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Helmet } from 'react-helmet'
import { RowMode, SimpleItemWithColData } from '../form/types'
import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs'
import {
addCss,
addJs,
autoNumber,
controlStyleCondition,
GridExtraFilterState,
setFormEditingExtraItemValues,
setGridPanelColor,
} from './Utils'
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
import { useFilters } from './useFilters'
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
import { getList } from '@/services/form.service'
import { layoutTypes } from '../admin/listForm/edit/types'
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
import { useListFormColumns } from './useListFormColumns'
import { DataType } from 'devextreme/common'
import { useStoreState } from '@/store/store'
import SubForms from '../form/SubForms'
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
import { workflowService } from '@/services/workflow.service'
import { NotePanel } from '../form/notes/NotePanel'
interface TreeProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)'
const isTemporaryDxKey = (key: unknown) => typeof key === 'string' && key.startsWith('_DX_KEY_')
const getPersistedInsertedKey = (e: any, keyFieldName?: string) => {
const dataKey = keyFieldName ? e.data?.[keyFieldName] : undefined
if (dataKey !== undefined && dataKey !== null && !isTemporaryDxKey(dataKey)) {
return dataKey
}
if (e.key !== undefined && e.key !== null && !isTemporaryDxKey(e.key)) {
return e.key
}
return undefined
}
const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => {
let changed = false
const expected = String(field || '').toLowerCase()
const nextItems = items.map((item) => {
const key = item?.dataField || item?.name
let nextItem = item
if (key && String(key).toLowerCase() === expected) {
const editorOptions = nextItem.editorOptions || {}
if (editorOptions.readOnly !== readOnly) {
changed = true
nextItem = {
...nextItem,
editorOptions: {
...editorOptions,
readOnly,
},
}
}
}
if (nextItem?.items?.length) {
const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly)
if (childResult.changed) {
changed = true
nextItem = { ...nextItem, items: childResult.items }
}
}
if (nextItem?.tabs?.length) {
const tabs = nextItem.tabs.map((tab: any) => {
const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly)
if (tabResult.changed) {
changed = true
return { ...tab, items: tabResult.items }
}
return tab
})
nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs }
}
return nextItem
})
return { items: nextItems, changed }
}
const findFormFieldKey = (items: any[] = [], field: string): string => {
const expected = String(field || '').toLowerCase()
for (const item of items || []) {
const key = item?.dataField || item?.name
if (key && String(key).toLowerCase() === expected) {
return key
}
const childKey = findFormFieldKey(item?.items || [], field)
if (childKey) {
return childKey
}
for (const tab of item?.tabs || []) {
const tabKey = findFormFieldKey(tab?.items || [], field)
if (tabKey) {
return tabKey
}
}
}
return field
}
const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => {
if (!form?.option) return false
const apply = () => {
const formItems = form.option('items') || []
const resolvedField = findFormFieldKey(formItems, field)
const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly)
if (result.changed) {
try {
const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field)
if (item) {
form.itemOption?.(resolvedField, 'editorOptions', {
...(item.editorOptions || {}),
readOnly,
})
} else {
form.option('items', result.items)
}
} catch {
form.option('items', result.items)
}
}
const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
if (activeEditor?.option?.('readOnly') !== readOnly) {
activeEditor?.option?.('readOnly', readOnly)
}
}
apply()
setTimeout(apply, 0)
return true
}
const getActiveEditingForm = (component: any) => {
const editForm = component?.getView?.('editingView')?._editForm
return editForm?.getEditor || editForm?.itemOption ? editForm : undefined
}
const flattenEditingFormItems = (items: any[] = []): EditingFormItemDto[] =>
items.flatMap((item) => [
...(item?.dataField ? [item] : []),
...flattenEditingFormItems(item?.items || []),
...(item?.tabs || []).flatMap((tab: any) => flattenEditingFormItems(tab?.items || [])),
])
const getValueByField = (data: Record<string, any> = {}, field?: string) => {
if (!field) return undefined
if (Object.prototype.hasOwnProperty.call(data, field)) return data[field]
const key = Object.keys(data).find(
(itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(),
)
return key ? data[key] : undefined
}
const shouldRunEditorScriptOnContentReady = (script?: string) =>
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
const Tree = (props: TreeProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
const { smaller } = useResponsive()
const currentUser = useStoreState((state) => state.auth.user)
const gridRef = useRef<TreeListRef>()
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const editingFormDataRef = useRef<Record<string, any>>({})
const editingFormInstanceRef = useRef<any>()
// Edit popup state kaydetmeyi engellemek için flag
const isEditingRef = useRef(false)
const [treeListDataSource, setTreeListDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>()
const [formData, setFormData] = useState<any>()
const [mode, setMode] = useState<RowMode>('view')
const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([])
const [gridDto, setGridDto] = useState<GridDto>()
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const [notePanelTarget, setNotePanelTarget] = useState<{
entityName: string
entityId: string
} | null>(null)
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
type EditorOptionsWithButtons = {
buttons?: any[]
} & Record<string, any>
const defaultSearchParams = useRef<string | null>(null)
useEffect(() => {
const initializeTreeList = async () => {
const response = await getList({ listFormCode })
setGridDto(response.data)
}
if (extGridDto === undefined) {
initializeTreeList()
} else {
setGridDto(extGridDto)
}
}, [listFormCode, extGridDto])
useEffect(() => {
if (!defaultSearchParams.current) {
defaultSearchParams.current = searchParams?.get('filter') ?? null
}
}, [searchParams])
// StateStoring için storageKey'i memoize et
const storageKey = useMemo(() => {
return gridDto?.gridOptions.stateStoringDto?.storageKey ?? ''
}, [gridDto?.gridOptions.stateStoringDto?.storageKey])
const customSaveState = useCallback(
(state: any) => {
if (isEditingRef.current) {
return Promise.resolve()
}
return postListFormCustomization({
listFormCode: listFormCode,
customizationType: ListFormCustomizationTypeEnum.GridState,
filterName: `tree-${storageKey}`,
customizationData: JSON.stringify(state),
})
.then(() => {
setGridPanelColor(statedGridPanelColor)
toast.push(
<Notification type="success" duration={2000}>
{translate('::ListForms.ListForm.GridStateSaved')}
</Notification>,
{
placement: 'top-end',
},
)
})
.catch((err) => {
toast.push(
<Notification type="danger" duration={2500}>
{translate('::ListForms.ListForm.GridStateSaveError')}
</Notification>,
{
placement: 'top-end',
},
)
throw err
})
},
[listFormCode, storageKey, translate],
)
const customLoadState = useCallback(() => {
return getListFormCustomization(
listFormCode,
ListFormCustomizationTypeEnum.GridState,
`tree-${storageKey}`,
).then((response: any) => {
if (response.data?.length > 0) {
setGridPanelColor(statedGridPanelColor)
return JSON.parse(response.data[0].customizationData)
}
// Veri yoksa null dön (DevExtreme bunu default state olarak algılar)
return null
})
}, [listFormCode, storageKey])
const { filterToolbarData, ...filterData } = useFilters({
gridDto,
gridRef,
listFormCode,
saveGridState: customSaveState,
})
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
const openNotePanel = useCallback(
(rowData: Record<string, any>) => {
const keyFieldName = gridDto?.gridOptions.keyFieldName
const entityId = getValueByField(rowData, keyFieldName)
if (entityId === undefined || entityId === null || entityId === '') {
return
}
setNotePanelTarget({
entityName: gridDto?.gridOptions.listFormCode ?? listFormCode,
entityId: String(entityId),
})
},
[gridDto, listFormCode],
)
const { getBandedColumns } = useListFormColumns({
gridDto,
listFormCode,
isSubForm,
gridRef,
onShowNote: openNotePanel,
})
const extractSearchParamsFields = useCallback((filter: any): [string, string, any][] => {
if (!Array.isArray(filter)) return []
if (filter.length === 3 && typeof filter[0] === 'string') {
return [filter as [string, string, any]]
}
return filter.flatMap((f) => extractSearchParamsFields(f))
}, [])
const getSelectedRowKeys = useCallback(async () => {
const tree = gridRef.current?.instance()
if (!tree) {
return []
}
return await tree.getSelectedRowKeys()
}, [])
const getSelectedRowsData = useCallback(() => {
const tree = gridRef.current?.instance()
if (!tree) {
return []
}
return tree.getSelectedRowsData()
}, [])
const expandAll = useCallback(() => {
const tree = gridRef.current?.instance()
if (!tree) return
tree.forEachNode((node: any) => {
if (node.hasChildren) {
tree.expandRow(node.key)
}
})
}, [])
const collapseAll = useCallback(() => {
const tree = gridRef.current?.instance()
if (!tree) return
tree.forEachNode((node: any) => {
if (node.hasChildren) {
tree.collapseRow(node.key)
}
})
}, [])
const updateWorkflowApprovalButtons = useCallback(
(component?: any, selectedRowsData?: Record<string, unknown>[]) => {
const tree = component ?? gridRef.current?.instance()
if (!tree) {
return
}
updateWorkflowApprovalToolbarItems(
tree,
gridDto?.gridOptions.workflowDto,
selectedRowsData ?? tree.getSelectedRowsData(),
currentUser,
)
},
[currentUser, gridDto],
)
const refreshData = useCallback(() => {
const tree = gridRef.current?.instance()
const refreshResult = tree?.refresh()
Promise.resolve(refreshResult).finally(() => updateWorkflowApprovalButtons(tree))
}, [updateWorkflowApprovalButtons])
const getFilter = useCallback(() => {
const tree = gridRef.current?.instance()
if (!tree) {
return
}
return tree.getCombinedFilter()
}, [])
const layout = layoutTypes.tree
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
expandAll,
collapseAll,
getFilter,
layout,
})
const onSelectionChanged = useCallback(
(data: any) => {
const treeOpt = gridDto?.gridOptions
const tree = gridRef.current?.instance()
if (!treeOpt || !tree) {
return
}
if (treeOpt.editingOptionDto?.allowDeleting) {
try {
const opt = tree.option('toolbar')
if (opt && opt.items && Array.isArray(opt.items)) {
const deleteSelectedRecordsIndex = opt.items
.map((e: any) => e.name)
.indexOf('deleteSelectedRecords')
if (deleteSelectedRecordsIndex >= 0) {
tree.option(
`toolbar.items[${deleteSelectedRecordsIndex}].options.visible`,
data.selectedRowsData.length > 1,
)
}
}
} catch (error) {
console.error('Error updating toolbar items:', error)
}
}
updateWorkflowApprovalButtons(tree, data.selectedRowsData)
if (data.selectedRowsData.length) {
setFormData(data.selectedRowsData[0])
}
},
[gridDto],
)
const onCellPrepared = useCallback(
(e: any) => {
const columnFormats = gridDto?.columnFormats
if (!columnFormats) {
return
}
for (let indxCol = 0; indxCol < columnFormats.length; indxCol++) {
const colFormat = columnFormats[indxCol]
for (let indxStyl = 0; indxStyl < colFormat.columnStylingDto.length; indxStyl++) {
const colStyle = colFormat.columnStylingDto[indxStyl]
if (e.rowType == colStyle.rowType) {
if (colStyle.useRow || e.column.dataField == colFormat.fieldName) {
if (
!colStyle.conditionValue ||
controlStyleCondition(e.data, colFormat.fieldName, colStyle)
) {
if (colStyle.cssClassName) {
e.cellElement.addClass(colStyle.cssClassName)
}
if (colStyle.cssStyles) {
e.cellElement.attr(
'style',
e.cellElement.attr('style') + ';' + colStyle.cssStyles,
)
}
}
}
}
}
}
},
[gridDto],
)
function onInitNewRow(e: any) {
if (!gridDto?.columnFormats) {
editingFormDataRef.current = { ...(e.data || {}) }
return
}
setMode('new')
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
for (const colFormat of gridDto?.columnFormats) {
if (!colFormat.fieldName) {
continue
}
// Grid'den gelen columnFormat'ları kullanarak default değerleri set et
if (colFormat.defaultValue != null) {
if (
typeof colFormat.defaultValue === 'string' &&
colFormat.defaultValue === '@AUTONUMBER'
) {
e.data[colFormat.fieldName] = autoNumber
} else {
e.data[colFormat.fieldName] = colFormat.defaultValue
}
}
if (extraFilters.some((f) => f.fieldName === colFormat.fieldName)) {
continue
}
if (!searchParams) {
continue
}
const rawFilter = searchParams?.get('filter')
if (rawFilter) {
const parsed = JSON.parse(rawFilter)
const filters = extractSearchParamsFields(parsed)
const fieldMatch = filters.find(([field]) => field === colFormat.fieldName)
if (fieldMatch) {
const val = fieldMatch[2] //value
const dType = colFormat.dataType as DataType
switch (dType) {
case 'date':
case 'datetime':
e.data[colFormat.fieldName] = new Date(val)
break
case 'number':
e.data[colFormat.fieldName] = Number(val)
break
case 'boolean':
e.data[colFormat.fieldName] = val === true || val === 'true'
break
case 'object':
try {
e.data[colFormat.fieldName] = JSON.parse(val)
} catch {}
break
default:
e.data[colFormat.fieldName] = val
break
}
}
}
editingFormDataRef.current = { ...(e.data || {}) }
}
}
const onRowInserting = useCallback((e: TreeListTypes.RowInsertingEvent) => {
e.data = setFormEditingExtraItemValues(e.data)
}, [])
const onRowUpdating = useCallback(
(e: TreeListTypes.RowUpdatingEvent) => {
if (gridDto?.gridOptions.editingOptionDto?.sendOnlyChangedFormValuesUpdate) {
if (Object.keys(e.newData).some((a) => a.includes(':'))) {
Object.keys(e.oldData).forEach((col) => {
if (col.includes(':')) {
e.newData[col] = e.newData[col] ?? e.oldData[col]
}
})
}
e.newData = setFormEditingExtraItemValues(e.newData)
} else {
let newData = { ...e.oldData, ...e.newData }
newData = setFormEditingExtraItemValues(newData)
Object.keys(newData).forEach((key) => {
if (key.includes(':')) {
delete newData[key]
}
})
e.newData = newData
}
if (gridDto?.gridOptions.keyFieldName) {
delete e.newData[gridDto?.gridOptions.keyFieldName]
}
},
[gridDto],
)
const onEditingStart = useCallback(
(e: TreeListTypes.EditingStartEvent) => {
isEditingRef.current = true
editingFormDataRef.current = { ...(e.data || {}) }
setMode('edit')
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
const columns = e.component.option('columns') as GridColumnData[]
columns?.forEach((col) => {
if (!col.dataField?.includes(':')) {
return
}
const field = col.dataField.split(':')
if (!e.data[field[0]]) {
return
}
const json = JSON.parse(e.data[field[0]])
e.data[col.dataField] = json[field[1]]
})
},
[gridDto],
)
const onDataErrorOccurred = useCallback((e: TreeListTypes.DataErrorOccurredEvent) => {
toast.push(
<Notification type="danger" duration={2000}>
{e.error?.message}
</Notification>,
{
placement: 'top-end',
},
)
}, [])
function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) {
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === editor.dataField)
const fieldName = editor.dataField.split(':')[0]
const columnFormat = gridDto.columnFormats.find((column) => column.fieldName === fieldName)
const isNewRow = Boolean((editor as any).row?.isNewRow) || mode === 'new'
const hasReadOnlyEditorOption = editor.editorOptions?.readOnly === true
const hasDisabledEditorOption = editor.editorOptions?.disabled === true
if (
(isNewRow && columnFormat?.allowAdding === false) ||
(!isNewRow && columnFormat?.allowEditing === false)
) {
editor.editorOptions.readOnly = true
} else if (isNewRow && columnFormat?.allowAdding === true) {
if (!hasReadOnlyEditorOption) {
editor.editorOptions.readOnly = false
}
if (!hasDisabledEditorOption) {
editor.editorOptions.disabled = false
}
}
// Cascade disabled mantığı
const colFormat = gridDto.columnFormats.find((c) => c.fieldName === editor.dataField)
if (colFormat?.lookupDto?.cascadeParentFields) {
const parentFields = colFormat.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const prevHandler = editor.editorOptions.onValueChanged
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
// Parent field değiştiğinde tüm cascade childları kontrol et
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
const rowData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}
// Bu field bir parent ise, child fieldleri temizle
if (colFormat.lookupDto?.cascadeEmptyFields) {
const childFields = colFormat.lookupDto.cascadeEmptyFields
.split(',')
.map((f: string) => f.trim())
childFields.forEach((childField: string) => {
if (rowIndex >= 0) {
grid.cellValue(rowIndex, childField, null)
}
})
}
// Tüm cascade fieldlerin disabled durumlarını güncelle
const popup = grid.option('editing.popup')
if (popup) {
const formInstance = grid.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !rowData[pf])
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === col.fieldName)
const editorOptions = formItem?.editorOptions
? JSON.parse(formItem.editorOptions)
: {}
editorInstance.option(
'disabled',
editorOptions?.disabled === true ? true : shouldDisable,
)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
}
}
})
}
}
}
// İlk açılışta disabled durumunu kontrol et
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}
const shouldDisable = parentFields.some((pf: string) => !rowData[pf])
if (shouldDisable) {
editor.editorOptions.disabled = true
}
}
if (formItem?.editorScript) {
const prevHandler = editor.editorOptions.onValueChanged
const editorDataField = editor.dataField
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
try {
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
const formData = {
...(grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}),
[editorDataField]: e.value,
}
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(
editingFormInstanceRef.current ?? getActiveEditingForm(grid),
field,
readOnly,
)
const setFormData = (newData: any) => {
if (rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
grid.cellValue(rowIndex, field, newData[field])
})
}
}
executeEditorScript(formItem.editorScript!, {
formData,
e,
editor,
runtimeSetEditorReadOnly,
setFormData,
})
} catch (err) {
console.error('Script exec error', formItem.dataField, err)
}
}
}
if (editor.editorOptions?.buttons) {
editor.editorOptions.buttons = editor.editorOptions.buttons.map((btn: any) => {
if (btn?.options?.onClick && typeof btn.options.onClick === 'function') {
const origClick = btn.options.onClick
btn.options.onClick = (e: any) => {
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
const formData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}
origClick({
...e,
formData,
fieldName: editor.dataField,
rowKey,
rowIndex,
})
}
}
return btn
})
}
}
}
useEffect(() => {
if (gridRef?.current) {
gridRef?.current?.instance().option('columns', undefined)
gridRef?.current?.instance().option('dataSource', undefined)
gridRef?.current?.instance().state(null)
}
if (refListFormCode.current !== listFormCode) {
setExtraFilters([])
}
}, [listFormCode])
// WidgetGroup yüksekliğini hesapla
useEffect(() => {
const calculateWidgetHeight = () => {
if (widgetGroupRef.current) {
const height = widgetGroupRef.current.offsetHeight
setWidgetGroupHeight(height)
}
}
// İlk render'da hesapla
calculateWidgetHeight()
// Resize durumunda tekrar hesapla
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
if (widgetGroupRef.current) {
resizeObserver.observe(widgetGroupRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [gridDto?.widgets])
useEffect(() => {
if (!gridDto) {
return
}
const treeOpt = gridDto.gridOptions
if (treeOpt.customJsSources.length) {
for (const js of treeOpt.customJsSources) {
addJs(js)
}
}
if (treeOpt.customStyleSources.length) {
for (const css of treeOpt.customStyleSources) {
addCss(css)
}
}
if (gridDto?.gridOptions.extraFilterDto) {
setExtraFilters(
gridDto.gridOptions.extraFilterDto.map((f) => ({
fieldName: f.fieldName,
operator: f.operator,
controlType: f.controlType,
value: f.defaultValue ?? '',
})),
)
}
if (gridDto.gridOptions.editingOptionDto?.popup) {
setIsPopupFullScreen(gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
}
// Set initial expanded row keys
if (gridDto.gridOptions.treeOptionDto?.expandedRowKeys) {
setExpandedRowKeys(gridDto.gridOptions.treeOptionDto.expandedRowKeys)
} else if (gridDto.gridOptions.treeOptionDto?.autoExpandAll) {
setExpandedRowKeys([])
}
}, [gridDto])
useEffect(() => {
if (!gridDto) return
const cols = getBandedColumns()
setColumnData(cols)
const dataSource = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layoutTypes.tree,
cols,
)
setTreeListDataSource(dataSource)
}, [gridDto, searchParams])
useEffect(() => {
const activeFilters = extraFilters.filter((f) => f.value)
let base: any = null
if (defaultSearchParams.current) {
base = JSON.parse(defaultSearchParams.current)
}
const baseTriplets = extractSearchParamsFields(base)
const extraTriplets = activeFilters.map(
(f) => [f.fieldName, f.operator, f.value] as [string, string, any],
)
const merged = [...baseTriplets, ...extraTriplets].reduce(
(acc, cur) => {
const [field, op] = cur
const idx = acc.findIndex((a) => a[0] === field && a[1] === op)
if (idx >= 0) {
acc[idx] = cur
} else {
acc.push(cur)
}
return acc
},
[] as [string, string, any][],
)
let filter: any = null
if (merged.length === 1) {
filter = merged[0]
} else if (merged.length > 1) {
filter = merged.reduce((acc, f, idx) => {
if (idx === 0) return f
return [acc, 'and', f]
}, null as any)
}
if (filter) {
searchParams?.set('filter', JSON.stringify(filter))
} else {
searchParams?.delete('filter')
}
gridRef.current?.instance().refresh()
}, [extraFilters])
useEffect(() => {
if (!columnData || !gridRef?.current) return
const instance = gridRef?.current?.instance()
if (instance) {
instance.option('columns', columnData as any)
}
}, [columnData])
useEffect(() => {
if (!treeListDataSource || !gridRef?.current) return
const instance = gridRef?.current?.instance()
if (instance) {
instance.option('dataSource', treeListDataSource)
}
}, [treeListDataSource])
useEffect(() => {
refListFormCode.current = listFormCode
}, [listFormCode])
// Component mount olduğunda state'i bir kez yükle
useEffect(() => {
if (!gridDto || !gridRef?.current || !treeListDataSource || !columnData) return
const instance = gridRef?.current?.instance()
if (instance) {
customLoadState()
.then((state) => {
if (state) {
instance.state(state)
}
})
.catch((err) => {
console.error('State load error:', err)
})
}
}, [gridDto, treeListDataSource, columnData])
// StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak
useEffect(() => {
if (!gridDto || !gridRef?.current) return
const instance = gridRef?.current?.instance()
if (instance) {
// Otomatik state kaydetme/yükleme kapalı
instance.option('stateStoring', {
enabled: false,
})
}
}, [gridDto])
return (
<>
<div ref={widgetGroupRef}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}>
{!isSubForm && (
<Helmet
titleTemplate={`%s | ${APP_NAME}`}
title={translate('::' + gridDto?.gridOptions.title)}
defaultTitle={APP_NAME}
></Helmet>
)}
{gridDto && columnData && (
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 ">
<TreeListDx
ref={gridRef as any}
id={'TreeList-' + listFormCode}
height={
gridDto.gridOptions.height > 0
? gridDto.gridOptions.height
: gridDto.gridOptions.fullHeight
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
width={gridDto.gridOptions.width || '100%'}
dataStructure="plain"
keyExpr={gridDto.gridOptions.treeOptionDto?.keyExpr}
parentIdExpr={gridDto.gridOptions.treeOptionDto?.parentIdExpr}
hasItemsExpr={gridDto.gridOptions.treeOptionDto?.hasItemsExpr}
rootValue={
gridDto.gridOptions.treeOptionDto?.rootValue === '' ||
gridDto.gridOptions.treeOptionDto?.rootValue === undefined
? null
: gridDto.gridOptions.treeOptionDto?.rootValue
}
{...(!gridDto.gridOptions.treeOptionDto?.autoExpandAll && {
expandedRowKeys: expandedRowKeys,
onRowExpanding: (e: any) => {
setExpandedRowKeys((prev) => [...prev, e.key])
},
onRowCollapsing: (e: any) => {
setExpandedRowKeys((prev) => prev.filter((k) => k !== e.key))
},
})}
autoExpandAll={gridDto.gridOptions.treeOptionDto?.autoExpandAll || false}
allowColumnResizing={gridDto.gridOptions.columnOptionDto?.allowColumnResizing}
allowColumnReordering={gridDto.gridOptions.columnOptionDto?.allowColumnReordering}
showBorders={gridDto.gridOptions.columnOptionDto?.showBorders}
showRowLines={gridDto.gridOptions.columnOptionDto?.showRowLines}
showColumnLines={gridDto.gridOptions.columnOptionDto?.showColumnLines}
columnResizingMode={gridDto.gridOptions.columnOptionDto?.columnResizingMode}
columnAutoWidth={gridDto.gridOptions.columnOptionDto?.columnAutoWidth}
rtlEnabled={gridDto.gridOptions.columnOptionDto?.rtlEnabled}
rowAlternationEnabled={gridDto.gridOptions.columnOptionDto?.rowAlternationEnabled}
onRowPrepared={(e) => {
//header, filter, data, group, summaries
if (e.rowType === 'data') {
e.rowElement.style.height = gridDto.gridOptions?.rowDto.rowHeight
e.rowElement.style.whiteSpace = gridDto.gridOptions?.rowDto.whiteSpace
e.rowElement.style.overflowWrap = gridDto.gridOptions?.rowDto.overflowWrap
}
}}
hoverStateEnabled={gridDto.gridOptions.columnOptionDto?.hoverStateEnabled}
columnHidingEnabled={gridDto.gridOptions.columnOptionDto?.columnHidingEnabled}
focusedRowEnabled={gridDto.gridOptions.columnOptionDto?.focusedRowEnabled}
showColumnHeaders={gridDto.gridOptions.columnOptionDto?.showColumnHeaders}
filterSyncEnabled={true}
onSelectionChanged={onSelectionChanged}
onInitNewRow={onInitNewRow}
onCellPrepared={onCellPrepared}
onRowInserting={onRowInserting}
onRowUpdating={onRowUpdating}
onEditingStart={onEditingStart}
onDataErrorOccurred={onDataErrorOccurred}
onEditCanceled={() => {
isEditingRef.current = false
setMode('view')
setIsPopupFullScreen(false)
}}
onSaved={() => {
isEditingRef.current = false
setMode('view')
setIsPopupFullScreen(false)
}}
onRowInserted={(e) => {
const insertedKey = getPersistedInsertedKey(e, gridDto.gridOptions.keyFieldName)
if (
gridDto.gridOptions.workflowDto?.approvalStatusFieldName &&
insertedKey !== undefined
) {
workflowService
.startWorkflow(listFormCode, [insertedKey])
.then((result) => {
const messages = result.toastMessages ?? []
if (messages.length > 0) {
toast.push(
<Notification type="info" duration={7000}>
{messages.map((message, messageIndex) => (
<div
key={messageIndex}
className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}
>
{message.split('\n').map((line, lineIndex) => (
<div key={lineIndex}>{line}</div>
))}
</div>
))}
</Notification>,
{ placement: 'top-end' },
)
}
gridRef.current?.instance()?.refresh()
})
.catch(console.error)
}
props.refreshData?.()
}}
onRowUpdated={() => {
props.refreshData?.()
}}
onRowRemoved={() => {
props.refreshData?.()
}}
onEditorPreparing={onEditorPreparing}
onContentReady={(e) => {
updateWorkflowApprovalButtons(e.component)
// Restore expanded keys after data refresh (only if autoExpandAll is false)
if (
!gridDto.gridOptions.treeOptionDto?.autoExpandAll &&
expandedRowKeys.length > 0
) {
e.component.option('expandedRowKeys', expandedRowKeys)
}
}}
>
<RemoteOperations filtering={true} sorting={true} grouping={false} />
<Editing
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
mode={smaller.md ? 'form' : gridDto.gridOptions.editingOptionDto?.mode}
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
useIcons={gridDto.gridOptions.editingOptionDto?.useIcons}
confirmDelete={gridDto.gridOptions.editingOptionDto?.confirmDelete}
selectTextOnEditStart={gridDto.gridOptions.editingOptionDto?.selectTextOnEditStart}
startEditAction={gridDto.gridOptions.editingOptionDto?.startEditAction}
popup={{
animation: {},
title:
(mode === 'new' ? '✚ ' : '🖊️ ') +
translate('::' + gridDto.gridOptions.editingOptionDto?.popup?.title),
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
hideOnOutsideClick:
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
fullScreen: isPopupFullScreen,
toolbarItems: [
{
widget: 'dxButton',
toolbar: 'bottom',
location: 'after',
options: {
text: translate('::Save'),
type: 'default',
onClick: () => {
const tree = gridRef.current?.instance()
tree?.saveEditData()
},
},
},
{
widget: 'dxButton',
toolbar: 'bottom',
location: 'after',
options: {
text: translate('::Cancel'),
onClick: () => {
const tree = gridRef.current?.instance()
tree?.cancelEditData()
},
},
},
{
widget: 'dxButton',
toolbar: 'top',
location: 'after',
options: {
icon: isPopupFullScreen ? 'collapse' : 'fullscreen',
hint: isPopupFullScreen
? translate('::Normal Boyut')
: translate('::Tam Ekran'),
stylingMode: 'text',
onClick: () => setIsPopupFullScreen(!isPopupFullScreen),
},
},
],
}}
form={{
colCount: 1,
onContentReady: (e) => {
editingFormInstanceRef.current = e.component
const form = e.component
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(form, field, readOnly)
const setFormData = (newData: any) => {
form?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
if (grid && rowIndex !== undefined && rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
grid.cellValue(rowIndex, field, newData[field])
})
}
}
const runReadOnlyScripts = () => {
const editorValues = gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.reduce<Record<string, any>>((values, formItem) => {
const editorInstance = form?.getEditor?.(formItem.dataField)
if (editorInstance?.option) {
values[formItem.dataField] = editorInstance.option('value')
}
return values
}, {})
const formData = {
...editingFormDataRef.current,
...(form?.option?.('formData') || {}),
...editorValues,
}
editingFormDataRef.current = { ...formData }
gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.filter((formItem) =>
shouldRunEditorScriptOnContentReady(formItem.editorScript),
)
.forEach((formItem) => {
try {
const editorInstance = form?.getEditor?.(formItem.dataField)
const editorValue =
editorInstance?.option?.('value') ??
getValueByField(formData, formItem.dataField)
const editor = {
dataField: formItem.dataField,
component: grid,
}
const e = {
component: form,
dataField: formItem.dataField,
value: editorValue,
}
executeEditorScript(formItem.editorScript!, {
formData,
e,
editor,
runtimeSetEditorReadOnly,
setFormData,
})
} catch (err) {
console.error('Script exec error on contentReady', formItem.dataField, err)
}
})
}
runReadOnlyScripts()
},
onFieldDataChanged: (e) => {
if (e.dataField) {
const previousValue = editingFormDataRef.current?.[e.dataField]
editingFormDataRef.current = {
...(e.component?.option?.('formData') || {}),
}
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.find(
(i) =>
String(i.dataField || '').toLowerCase() ===
String(e.dataField || '').toLowerCase(),
)
if (formItem?.editorScript) {
try {
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex =
rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const formData = {
...(e.component?.option?.('formData') || {}),
[e.dataField]: e.value,
}
editingFormDataRef.current = { ...formData }
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(e.component, field, readOnly)
const setFormData = (newData: any) => {
e.component?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
if (grid && rowIndex !== undefined && rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
grid.cellValue(rowIndex, field, newData[field])
})
}
}
executeEditorScript(formItem.editorScript, {
formData,
e,
editor: {
dataField: e.dataField,
component: grid,
},
runtimeSetEditorReadOnly,
setFormData,
})
} catch (err) {
console.error('Script exec error', err)
}
}
}
},
items:
gridDto.gridOptions.editingFormDto?.length > 0
? (() => {
const sortedFormDto = gridDto.gridOptions.editingFormDto
.slice()
.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
// Tabbed item'ları grupla
const tabbedItems = sortedFormDto.filter(
(e: any) => e.itemType === 'tabbed',
)
const result: any[] = []
// Helper function: item mapper
const mapFormItem = (i: EditingFormItemDto) => {
let editorOptions: EditorOptionsWithButtons = {}
let parsedEditorOptions: EditorOptionsWithButtons = {}
const forcedEditorOptions: EditorOptionsWithButtons = {}
try {
parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {}
const rawFilter = searchParams?.get('filter')
if (rawFilter) {
const parsed = JSON.parse(rawFilter)
const filters = extractSearchParamsFields(parsed)
const hasFilter = filters.some(
([field, op, val]) => field === i.dataField,
)
if (hasFilter) {
const existsInExtra = extraFilters.some(
(f) => f.fieldName === i.dataField && !!f.value,
)
if (!existsInExtra) {
forcedEditorOptions.readOnly = true
}
}
}
} catch {}
const fieldName = i.dataField.split(':')[0]
const listFormField = gridDto.columnFormats.find(
(x: any) => x.fieldName === fieldName,
)
const defaultEditorOptions: EditorOptionsWithButtons = {}
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
Object.assign(defaultEditorOptions, {
type: 'date',
dateSerializationFormat: 'yyyy-MM-dd',
displayFormat: 'shortDate',
})
} else if (
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
) {
Object.assign(defaultEditorOptions, {
type: 'datetime',
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
displayFormat: 'shortDateShortTime',
})
}
editorOptions = {
...defaultEditorOptions,
...parsedEditorOptions,
...forcedEditorOptions,
}
if (editorOptions?.buttons) {
editorOptions.buttons = (editorOptions?.buttons || []).map(
(btn: any) => {
if (
btn?.options?.onClick &&
typeof btn.options.onClick === 'string'
) {
btn.options.onClick = eval(`(${btn.options.onClick})`)
}
return btn
},
)
}
const item: SimpleItemWithColData = {
canRead: listFormField?.canRead ?? false,
canUpdate: listFormField?.canUpdate ?? false,
canCreate: listFormField?.canCreate ?? false,
canExport: listFormField?.canExport ?? false,
allowEditing: listFormField?.allowEditing ?? true,
allowAdding: listFormField?.allowAdding ?? true,
dataField: i.dataField,
name: i.dataField,
editorType2: i.editorType2,
editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox
? 'dxDropDownBox'
: i.editorType2 == PlatformEditorTypes.dxImageUpload
? undefined
: i.editorType2,
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions,
editorScript: i.editorScript,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }
}
if (
(mode == 'edit' && !item.canUpdate) ||
(mode == 'new' && !item.canCreate)
) {
item.editorOptions = {
...item.editorOptions,
readOnly: true,
}
}
return item
}
sortedFormDto.forEach((e: any) => {
if (e.itemType !== 'tabbed') {
result.push({
itemType: e.itemType,
colCount: e.colCount,
colSpan: e.colSpan,
caption: e.caption, // Group'larda caption olmalı
items: e.items
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
.map(mapFormItem)
.filter((a: any) => {
if (mode === 'view') {
return a.canRead
} else if (mode === 'new') {
return a.canCreate && a.allowAdding
} else if (mode === 'edit') {
return a.canUpdate && a.allowEditing
} else {
return false
}
}),
})
} else if (tabbedItems.length > 0 && e === tabbedItems[0]) {
// Tabbed için caption OLMAMALI - sadece tabs array içinde title kullan
result.push({
itemType: 'tabbed',
colCount: 1,
colSpan: 1,
// caption kullanma! Tabs içindeki title'lar yeterli
tabs: tabbedItems.map((tabbedItem: any) => {
// Backend'den gelen colCount ve colSpan değerlerini kullan
const effectiveColCount = tabbedItem.colCount || 1
return {
title: tabbedItem.caption, // Her tab'ın title'ı
colCount: effectiveColCount,
items: tabbedItem.items
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
.map(mapFormItem)
.filter((a: any) => {
if (mode === 'view') {
return a.canRead
} else if (mode === 'new') {
return a.canCreate && a.allowAdding
} else if (mode === 'edit') {
return a.canUpdate && a.allowEditing
} else {
return false
}
}),
}
}),
})
}
})
return result
})()
: undefined,
}}
></Editing>
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
<Template name="extraFilters">
<GridExtraFilterToolbar
filters={gridDto?.gridOptions.extraFilterDto ?? []}
extraFilters={extraFilters}
setExtraFilters={setExtraFilters}
/>
</Template>
<Toolbar
visible={(toolbarData?.length ?? 0) > 0 || (filterToolbarData?.length ?? 0) > 0}
>
{toolbarData?.map((item) => (
<Item key={item.name} {...item}></Item>
))}
{filterToolbarData?.map((item) => (
<Item key={item.name} {...item}></Item>
))}
{/* burada özel filtre alanını Template ile bağla */}
{gridDto?.gridOptions.extraFilterDto?.length ? (
<Item location="before" template="extraFilters" cssClass="no-default" />
) : null}
</Toolbar>
<Sorting mode={gridDto.gridOptions?.sortMode}></Sorting>
<FilterRow
visible={gridDto.gridOptions.filterRowDto?.visible}
applyFilter={gridDto.gridOptions.filterRowDto?.applyFilter}
></FilterRow>
<FilterPanel visible={gridDto.gridOptions.filterPanelDto.visible}></FilterPanel>
<HeaderFilter visible={gridDto.gridOptions.headerFilterDto.visible}></HeaderFilter>
<SearchPanel
visible={gridDto.gridOptions.searchPanelDto.visible}
width={gridDto.gridOptions.searchPanelDto.width}
placeholder={translate('::App.Search')}
></SearchPanel>
<Selection
mode={gridDto.gridOptions.selectionDto?.mode}
recursive={gridDto.gridOptions.treeOptionDto?.recursiveSelection || false}
></Selection>
<Paging enabled={true} defaultPageSize={gridDto.gridOptions.pageSize ?? 10} />
<Pager
visible={gridDto.gridOptions.pagerOptionDto?.visible ?? true}
allowedPageSizes={
gridDto.gridOptions.pagerOptionDto?.allowedPageSizes
?.split(',')
.map((a: any) => +a) ?? [10, 20, 50, 100]
}
showPageSizeSelector={
gridDto.gridOptions.pagerOptionDto?.showPageSizeSelector ?? true
}
showInfo={gridDto.gridOptions.pagerOptionDto?.showInfo ?? true}
showNavigationButtons={
gridDto.gridOptions.pagerOptionDto?.showNavigationButtons ?? true
}
infoText={gridDto.gridOptions.pagerOptionDto?.infoText}
displayMode={gridDto.gridOptions.pagerOptionDto?.displayMode ?? 'full'}
></Pager>
<ColumnChooser
enabled={gridDto.gridOptions.columnOptionDto?.columnChooserEnabled}
mode={gridDto.gridOptions.columnOptionDto?.columnChooserMode}
></ColumnChooser>
<ColumnFixing
enabled={gridDto.gridOptions.columnOptionDto?.columnFixingEnabled}
></ColumnFixing>
<Scrolling mode={gridDto.gridOptions.pagerOptionDto?.scrollingMode}></Scrolling>
<LoadPanel
enabled={gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled}
text={gridDto.gridOptions.pagerOptionDto?.loadPanelText}
></LoadPanel>
</TreeListDx>
{gridDto?.gridOptions?.subFormsDto?.length > 0 &&
gridDto?.gridOptions?.subFormsListFormType === SubFormTabTypeEnum.List && (
<>
<hr className="my-2" />
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} />
</>
)}
<Dialog
width={smaller.md ? '100%' : 1000}
isOpen={filterData.isImportModalOpen || false}
onClose={() => filterData.setIsImportModalOpen(false)}
onRequestClose={() => filterData.setIsImportModalOpen(false)}
>
<Dialog.Body className="flex flex-col">
<ImportDashboard gridDto={gridDto} />
</Dialog.Body>
</Dialog>
</div>
)}
<Dialog
isOpen={toolbarModalData?.open || false}
onClose={() => setToolbarModalData(undefined)}
onRequestClose={() => setToolbarModalData(undefined)}
>
{toolbarModalData?.content}
</Dialog>
<GridFilterDialogs gridRef={gridRef as any} listFormCode={listFormCode} {...filterData} />
{notePanelTarget && (
<NotePanel
entityName={notePanelTarget.entityName}
entityId={notePanelTarget.entityId}
isVisible
onToggle={() => setNotePanelTarget(null)}
/>
)}
</Container>
</>
)
}
export default Tree