sozsoft-platform/ui/src/views/list/Tree.tsx
2026-08-14 17:03:34 +03:00

747 lines
26 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 '@/utils/registerDevExtremeEditors'
import { Dialog, Notification, toast } from '@/components/ui'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Template } from 'devextreme-react/core/template'
import TreeListDx, {
ColumnChooser,
ColumnFixing,
Editing,
FilterPanel,
FilterRow,
HeaderFilter,
LoadPanel,
Pager,
Paging,
RemoteOperations,
Scrolling,
SearchPanel,
Selection,
Sorting,
Toolbar,
type TreeListRef,
type 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 PageTitle from '@/components/shared/PageTitle'
import { RowMode } from '../form/types'
import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs'
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
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 { layoutTypes } from '../admin/listForm/edit/types'
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
import { useListFormColumns } from './useListFormColumns'
import { orderListToolbarItems } from './toolbarOrder'
import { useStoreState } from '@/store/store'
import SubForms from '../form/SubForms'
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
import { NotePanel } from '../form/notes/NotePanel'
import { useListFormStateStoring } from './useListFormStateStoring'
import { buildEditPopupOptions, shouldUseMobileEditPopup } from './shared/editPopup'
import {
applyNewRowDefaults,
buildEditingFormItems,
buildInsertPayload,
buildUpdatePayload,
createFormItemMapper,
expandJsonFormFields,
getActiveEditingForm,
getValueByField,
} from './shared/editingForm'
import {
attachEditorButtonContext,
attachEditorValueChangedScript,
createEditingFormHandlers,
} from './shared/editingFormScripts'
import { attachCascadeBehavior, createCascadeMaps } from './shared/cascade'
import { createConditionalCellStyleHandler } from './shared/cellStyles'
import {
useExtraFilters,
useListFormCustomSources,
useListFormGridDto,
useWidgetGroupHeight,
} from './shared/hooks'
import { startWorkflowForInsertedRow } from './shared/workflowRows'
interface TreeProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const defaultPageSizes = [10, 20, 50, 100]
const Tree = (props: TreeProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
const currentUser = useStoreState((state) => state.auth.user)
const currentCulture = useStoreState(
(state) => state.abpConfig.config?.localization.currentCulture.cultureName,
)
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
const useMobileEditPopup = shouldUseMobileEditPopup()
const gridRef = useRef<TreeListRef | undefined>(undefined)
const widgetGroupRef = useRef<HTMLDivElement>(null)
const editingFormDataRef = useRef<Record<string, any>>({})
const editingFormInstanceRef = useRef<any | undefined>(undefined)
// Edit popup açıkken grid state'inin kaydedilmesini engeller.
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 gridDto = useListFormGridDto(listFormCode, extGridDto)
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
const [notePanelTarget, setNotePanelTarget] = useState<{
entityName: string
entityId: string
} | null>(null)
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, gridDto?.widgets)
useListFormCustomSources(gridDto)
const getTreeInstance = useCallback(() => gridRef.current?.instance(), [])
const { customSaveState, customLoadState } = useListFormStateStoring({
listFormCode,
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
filterPrefix: 'tree',
skipSaveRef: isEditingRef,
})
const { filterToolbarData, ...filterData } = useFilters({
gridDto,
gridRef,
listFormCode,
saveGridState: customSaveState,
searchParams,
})
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
const refreshTree = useCallback(() => {
getTreeInstance()?.refresh()
}, [getTreeInstance])
const { extraFilters, setExtraFilters } = useExtraFilters({
gridDto,
listFormCode,
searchParams,
onFilterChanged: refreshTree,
})
const openNotePanel = useCallback(
(rowData: Record<string, any>) => {
const entityId = getValueByField(rowData, gridDto?.gridOptions.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,
})
/* =======================
TOOLBAR
======================= */
const updateWorkflowApprovalButtons = useCallback(
(component?: any, selectedRowsData?: Record<string, unknown>[]) => {
const tree = component ?? getTreeInstance()
if (!tree) return
updateWorkflowApprovalToolbarItems(
tree,
gridDto?.gridOptions.workflowDto,
selectedRowsData ?? tree.getSelectedRowsData(),
currentUser,
)
},
[currentUser, getTreeInstance, gridDto],
)
const refreshData = useCallback(() => {
const tree = getTreeInstance()
Promise.resolve(tree?.refresh()).finally(() => updateWorkflowApprovalButtons(tree))
}, [getTreeInstance, updateWorkflowApprovalButtons])
const getSelectedRowKeys = useCallback(
async () => (await getTreeInstance()?.getSelectedRowKeys()) ?? [],
[getTreeInstance],
)
const getSelectedRowsData = useCallback(
() => getTreeInstance()?.getSelectedRowsData() ?? [],
[getTreeInstance],
)
const getFilter = useCallback(() => getTreeInstance()?.getCombinedFilter(), [getTreeInstance])
const toggleAllNodes = useCallback(
(expand: boolean) => {
const tree = getTreeInstance()
if (!tree) return
tree.forEachNode((node: any) => {
if (!node.hasChildren) return
if (expand) {
tree.expandRow(node.key)
} else {
tree.collapseRow(node.key)
}
})
},
[getTreeInstance],
)
const expandAll = useCallback(() => toggleAllNodes(true), [toggleAllNodes])
const collapseAll = useCallback(() => toggleAllNodes(false), [toggleAllNodes])
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
expandAll,
collapseAll,
getFilter,
layout: layoutTypes.tree,
})
const orderedToolbarData = useMemo(
() => orderListToolbarItems([...toolbarData, ...filterToolbarData]),
[toolbarData, filterToolbarData],
)
/* =======================
TREE EVENTS
======================= */
const onSelectionChanged = useCallback(
(data: any) => {
const tree = getTreeInstance()
if (!gridDto?.gridOptions || !tree) return
if (gridDto.gridOptions.editingOptionDto?.allowDeleting) {
const items = tree.option('toolbar')?.items ?? []
const deleteSelectedIndex = items.findIndex(
(item: any) => item.name === 'deleteSelectedRecords',
)
if (deleteSelectedIndex >= 0) {
tree.option(
`toolbar.items[${deleteSelectedIndex}].options.visible`,
data.selectedRowsData.length > 1,
)
}
}
updateWorkflowApprovalButtons(tree, data.selectedRowsData)
if (data.selectedRowsData.length) {
setFormData(data.selectedRowsData[0])
}
},
[getTreeInstance, gridDto, updateWorkflowApprovalButtons],
)
const onCellPrepared = useMemo(() => createConditionalCellStyleHandler(gridDto), [gridDto])
const onInitNewRow = useCallback(
(e: TreeListTypes.InitNewRowEvent) => {
if (!gridDto?.columnFormats) {
editingFormDataRef.current = { ...(e.data || {}) }
return
}
setMode('new')
setIsPopupFullScreen(
useMobileEditPopup || (gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
)
e.promise = applyNewRowDefaults(
gridDto,
e.data,
searchParams?.get('filter'),
extraFilters,
).then((data) => {
editingFormDataRef.current = { ...data }
})
},
[gridDto, searchParams, extraFilters, useMobileEditPopup],
)
const onRowInserting = useCallback(
(e: TreeListTypes.RowInsertingEvent) => {
e.data = buildInsertPayload(gridDto, e.data)
},
[gridDto],
)
const onRowUpdating = useCallback(
(e: TreeListTypes.RowUpdatingEvent) => {
if (!gridDto?.columnFormats) return
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
},
[gridDto],
)
const onEditingStart = useCallback(
(e: TreeListTypes.EditingStartEvent) => {
isEditingRef.current = true
editingFormDataRef.current = { ...(e.data || {}) }
setMode('edit')
setIsPopupFullScreen(
useMobileEditPopup || (gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
)
expandJsonFormFields(e.component.option('columns') as GridColumnData[], e.data)
},
[gridDto, useMobileEditPopup],
)
const onEditFinished = useCallback(() => {
isEditingRef.current = false
setMode('view')
setIsPopupFullScreen(false)
}, [])
const onDataErrorOccurred = useCallback((e: TreeListTypes.DataErrorOccurredEvent) => {
toast.push(
<Notification type="danger" duration={2000}>
{e.error?.message}
</Notification>,
{ placement: 'bottom-end' },
)
}, [])
const cascadeMaps = useMemo(() => createCascadeMaps(gridDto), [gridDto])
const onEditorPreparing = useCallback(
(editor: TreeListTypes.EditorPreparingEvent) => {
if (editor.parentType !== 'dataRow' || !editor.dataField || !gridDto) return
const columnFormat = gridDto.columnFormats.find(
(column) => column.fieldName === editor.dataField,
)
const isNewRow = Boolean((editor as any).row?.isNewRow) || mode === 'new'
if (columnFormat?.editorOptions) {
try {
editor.editorOptions = {
...editor.editorOptions,
...JSON.parse(columnFormat.editorOptions),
}
} catch (err) {
console.error('EditorOptions parse error:', columnFormat.fieldName, err)
}
}
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
}
const cascadeInfo = cascadeMaps.cascadeFieldsMap.get(editor.dataField)
if (cascadeInfo) {
attachCascadeBehavior({ editor, gridDto, cascadeInfo, maps: cascadeMaps })
}
if (columnFormat?.editOrderNo != null && columnFormat.editorScript) {
attachEditorValueChangedScript({
editor,
editorScript: columnFormat.editorScript,
fieldName: columnFormat.fieldName,
editingFormInstanceRef,
getActiveEditingForm,
})
}
attachEditorButtonContext(editor)
},
[gridDto, cascadeMaps, mode],
)
/* =======================
COLUMNS & DATASOURCE
======================= */
useEffect(() => {
setTreeListDataSource(undefined)
setColumnData(undefined)
const instance = getTreeInstance()
if (instance) {
instance.option('columns', undefined)
instance.option('dataSource', undefined)
instance.state(null)
}
}, [listFormCode, getTreeInstance])
useEffect(() => {
if (!gridDto) return
const columns = getBandedColumns()
setColumnData(columns)
setTreeListDataSource(
createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layoutTypes.tree,
columns,
),
)
// getBandedColumns her render'da yeni referans üretebildiği için bağımlılığa alınmaz.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [gridDto, listFormCode, searchParams, currentCulture, localizationTexts])
// Başlangıçta açık olacak düğümler.
useEffect(() => {
setExpandedRowKeys(gridDto?.gridOptions.treeOptionDto?.expandedRowKeys ?? [])
}, [gridDto])
useEffect(() => {
const instance = getTreeInstance()
if (columnData && instance) {
instance.option('columns', columnData as any)
}
}, [columnData, getTreeInstance])
useEffect(() => {
const instance = getTreeInstance()
if (treeListDataSource && instance) {
instance.option('dataSource', treeListDataSource)
}
}, [treeListDataSource, getTreeInstance])
// Otomatik state kaydetme/yükleme kapalı; manuel akış kullanılır.
useEffect(() => {
if (!gridDto) return
getTreeInstance()?.option('stateStoring', { enabled: false })
}, [gridDto, getTreeInstance])
useEffect(() => {
const instance = getTreeInstance()
if (!gridDto || !instance || !treeListDataSource || !columnData) return
customLoadState()
.then((state) => {
if (state) instance.state(state)
})
.catch((err) => console.error('State load error:', err))
}, [gridDto, treeListDataSource, columnData, customLoadState, getTreeInstance])
/* =======================
EDITING FORM
======================= */
const mapFormItem = useMemo(
() =>
createFormItemMapper({
mode,
translate,
rawFilter: searchParams?.get('filter'),
extraFilters,
getEditingFormData: () => editingFormDataRef.current,
}),
[mode, translate, searchParams, extraFilters],
)
const editingFormItems = useMemo(
() => buildEditingFormItems(gridDto, mapFormItem, mode),
[gridDto, mapFormItem, mode],
)
const editingFormHandlers = useMemo(
() =>
createEditingFormHandlers({
getGridDto: () => gridDto,
getGridInstance: getTreeInstance,
editingFormDataRef,
editingFormInstanceRef,
}),
[gridDto, getTreeInstance],
)
const editPopupOptions = useMemo(
() =>
buildEditPopupOptions({
popupOptions: gridDto?.gridOptions.editingOptionDto?.popup,
mode,
isPopupFullScreen,
useMobileEditPopup,
translate,
handlers: {
onSave: () => getTreeInstance()?.saveEditData(),
onCancel: () => getTreeInstance()?.cancelEditData(),
onToggleFullScreen: () => setIsPopupFullScreen((current) => !current),
},
}),
[gridDto, mode, isPopupFullScreen, useMobileEditPopup, translate, getTreeInstance],
)
/* =======================
RENDER
======================= */
const gridOptions = gridDto?.gridOptions
const treeOptions = gridOptions?.treeOptionDto
const autoExpandAll = treeOptions?.autoExpandAll ?? false
return (
<>
<div ref={widgetGroupRef} className={(gridDto?.widgets?.length ?? 0) > 0 ? 'mt-2' : ''}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}>
{!isSubForm && <PageTitle title={translate('::' + gridOptions?.title)} />}
{gridDto && gridOptions && columnData && (
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700">
<TreeListDx
ref={gridRef as any}
id={'TreeList-' + listFormCode}
className="list-view-toolbar-host"
height={
gridOptions.height > 0
? gridOptions.height
: gridOptions.fullHeight
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
width={gridOptions.width || '100%'}
dataStructure="plain"
keyExpr={treeOptions?.keyExpr}
parentIdExpr={treeOptions?.parentIdExpr}
hasItemsExpr={treeOptions?.hasItemsExpr}
rootValue={
treeOptions?.rootValue === '' || treeOptions?.rootValue === undefined
? null
: treeOptions.rootValue
}
{...(!autoExpandAll && {
expandedRowKeys,
onRowExpanding: (e: any) => setExpandedRowKeys((prev) => [...prev, e.key]),
onRowCollapsing: (e: any) =>
setExpandedRowKeys((prev) => prev.filter((key) => key !== e.key)),
})}
autoExpandAll={autoExpandAll}
allowColumnResizing={gridOptions.columnOptionDto?.allowColumnResizing}
allowColumnReordering={gridOptions.columnOptionDto?.allowColumnReordering}
showBorders={gridOptions.columnOptionDto?.showBorders}
showRowLines={gridOptions.columnOptionDto?.showRowLines}
showColumnLines={gridOptions.columnOptionDto?.showColumnLines}
columnResizingMode={gridOptions.columnOptionDto?.columnResizingMode}
columnAutoWidth={gridOptions.columnOptionDto?.columnAutoWidth}
rtlEnabled={gridOptions.columnOptionDto?.rtlEnabled}
rowAlternationEnabled={gridOptions.columnOptionDto?.rowAlternationEnabled}
hoverStateEnabled={gridOptions.columnOptionDto?.hoverStateEnabled}
columnHidingEnabled={gridOptions.columnOptionDto?.columnHidingEnabled}
focusedRowEnabled={gridOptions.columnOptionDto?.focusedRowEnabled}
showColumnHeaders={gridOptions.columnOptionDto?.showColumnHeaders}
filterSyncEnabled
onRowPrepared={(e) => {
if (e.rowType === 'data') {
e.rowElement.style.height = gridOptions.rowDto.rowHeight
e.rowElement.style.whiteSpace = gridOptions.rowDto.whiteSpace
e.rowElement.style.overflowWrap = gridOptions.rowDto.overflowWrap
}
}}
onSelectionChanged={onSelectionChanged}
onInitNewRow={onInitNewRow}
onCellPrepared={onCellPrepared}
onRowInserting={onRowInserting}
onRowUpdating={onRowUpdating}
onEditingStart={onEditingStart}
onDataErrorOccurred={onDataErrorOccurred}
onEditCanceled={onEditFinished}
onSaved={onEditFinished}
onRowInserted={(e) => {
startWorkflowForInsertedRow(e, gridDto, listFormCode, refreshTree)
props.refreshData?.()
}}
onRowUpdated={() => props.refreshData?.()}
onRowRemoved={() => props.refreshData?.()}
onEditorPreparing={onEditorPreparing}
onContentReady={(e) => {
updateWorkflowApprovalButtons(e.component)
// Veri yenilendikten sonra açık düğümleri geri yükle.
if (!autoExpandAll && expandedRowKeys.length > 0) {
e.component.option('expandedRowKeys', expandedRowKeys)
}
}}
>
<RemoteOperations filtering sorting grouping={false} />
<Editing
refreshMode={gridOptions.editingOptionDto?.refreshMode}
mode={gridOptions.editingOptionDto?.mode}
allowDeleting={gridOptions.editingOptionDto?.allowDeleting}
allowUpdating={gridOptions.editingOptionDto?.allowUpdating}
allowAdding={gridOptions.editingOptionDto?.allowAdding}
useIcons={gridOptions.editingOptionDto?.useIcons}
confirmDelete={gridOptions.editingOptionDto?.confirmDelete}
selectTextOnEditStart={gridOptions.editingOptionDto?.selectTextOnEditStart}
startEditAction={gridOptions.editingOptionDto?.startEditAction}
popup={editPopupOptions}
form={{
colCount: 1,
onContentReady: editingFormHandlers.onContentReady,
onFieldDataChanged: editingFormHandlers.onFieldDataChanged,
items: editingFormItems,
}}
/>
<Template name="cellEditTagBox" render={TagBoxEditorComponent} />
<Template name="cellEditGridBox" render={GridBoxEditorComponent} />
<Template name="cellEditImageUpload" render={ImageUploadEditorComponent} />
<Template
name="cellEditImageViewer"
render={(data: any) => (
<ImageViewerEditorComponent
{...data}
fallbackFormData={editingFormDataRef.current}
/>
)}
/>
<Template name="extraFilters">
<GridExtraFilterToolbar
filters={gridOptions.extraFilterDto ?? []}
extraFilters={extraFilters}
setExtraFilters={setExtraFilters}
/>
</Template>
<Toolbar visible={orderedToolbarData.length > 0}>
{orderedToolbarData.map((item) => (
<Item key={item.name} {...item} />
))}
{gridOptions.extraFilterDto?.length ? (
<Item location="before" template="extraFilters" cssClass="no-default" />
) : null}
</Toolbar>
<Sorting mode={gridOptions.sortMode} />
<FilterRow
visible={gridOptions.filterRowDto?.visible}
applyFilter={gridOptions.filterRowDto?.applyFilter}
/>
<FilterPanel visible={gridOptions.filterPanelDto.visible} />
<HeaderFilter visible={gridOptions.headerFilterDto.visible} />
<SearchPanel
visible={gridOptions.searchPanelDto.visible}
width={gridOptions.searchPanelDto.width}
placeholder={translate('::App.Platform.Search2')}
/>
<Selection
mode={gridOptions.selectionDto?.mode}
recursive={treeOptions?.recursiveSelection || false}
/>
<Paging enabled defaultPageSize={gridOptions.pageSize ?? 10} />
<Pager
visible={gridOptions.pagerOptionDto?.visible ?? true}
allowedPageSizes={
gridOptions.pagerOptionDto?.allowedPageSizes?.split(',').map(Number) ??
defaultPageSizes
}
showPageSizeSelector={gridOptions.pagerOptionDto?.showPageSizeSelector ?? true}
showInfo={gridOptions.pagerOptionDto?.showInfo ?? true}
showNavigationButtons={gridOptions.pagerOptionDto?.showNavigationButtons ?? true}
infoText={gridOptions.pagerOptionDto?.infoText}
displayMode={gridOptions.pagerOptionDto?.displayMode ?? 'full'}
/>
<ColumnChooser
enabled={gridOptions.columnOptionDto?.columnChooserEnabled}
mode={gridOptions.columnOptionDto?.columnChooserMode}
/>
<ColumnFixing enabled={gridOptions.columnOptionDto?.columnFixingEnabled} />
<Scrolling mode={gridOptions.pagerOptionDto?.scrollingMode} />
<LoadPanel
enabled={gridOptions.pagerOptionDto?.loadPanelEnabled}
text={gridOptions.pagerOptionDto?.loadPanelText}
/>
</TreeListDx>
{gridOptions.subFormsDto?.length > 0 &&
gridOptions.subFormsListFormType === SubFormTabTypeEnum.List && (
<>
<hr className="my-2" />
<SubForms gridDto={gridDto} formData={formData} level={level ?? 0} />
</>
)}
<Dialog
width={useMobileEditPopup ? '100%' : 1000}
isOpen={filterData.isImportModalOpen || false}
onClose={() => filterData.setIsImportModalOpen(false)}
onRequestClose={() => filterData.setIsImportModalOpen(false)}
>
<Dialog.Header className="-mx-6 -mt-6 flex h-11 items-center px-6 pr-20 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-800 dark:text-gray-100">
{translate('::App.ListForm.ImportManager')}
</h2>
</Dialog.Header>
<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