sozsoft-platform/ui/src/views/list/Grid.tsx
2026-10-01 11:50:16 +03:00

813 lines
29 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 DataGrid, {
ColumnChooser,
ColumnFixing,
type DataGridRef,
type DataGridTypes,
Editing,
Export,
FilterPanel,
FilterRow,
Grouping,
GroupItem as GroupItemDx,
GroupPanel,
HeaderFilter,
LoadPanel,
Pager,
Paging,
Scrolling,
SearchPanel,
Selection,
Sorting,
Summary,
Toolbar,
TotalItem,
} from 'devextreme-react/data-grid'
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 SubForms from '../form/SubForms'
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 { ImportDashboard } from '@/components/importManager/ImportDashboard'
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 { Loading } from '@/components/shared'
import { useStoreState } from '@/store'
import { NotePanel } from '../form/notes/NotePanel'
import { useListFormStateStoring } from './useListFormStateStoring'
import { exportListView } from './shared/exportListView'
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,
useListFormWidgets,
useWidgetGroupHeight,
} from './shared/hooks'
import { startWorkflowForInsertedRow } from './shared/workflowRows'
import { applyPhoneGlobalFormat } from './shared/columnFormatters'
interface GridProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const remoteOperations = {
paging: true,
filtering: true,
sorting: true,
grouping: false,
summary: false,
}
const Grid = (props: GridProps) => {
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<DataGridRef | 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 [gridDataSource, setGridDataSource] = 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 [notePanelTarget, setNotePanelTarget] = useState<{
entityName: string
entityId: string
} | null>(null)
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
useListFormCustomSources(gridDto)
const getGridInstance = useCallback(() => gridRef.current?.instance(), [])
const { customSaveState, customLoadState } = useListFormStateStoring({
listFormCode,
storageKey: gridDto?.gridOptions?.stateStoringDto?.storageKey,
filterPrefix: 'list',
skipSaveRef: isEditingRef,
})
const { filterToolbarData, ...filterData } = useFilters({
gridDto,
gridRef,
listFormCode,
saveGridState: customSaveState,
searchParams,
})
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
const refreshGrid = useCallback(() => {
getGridInstance()?.refresh()
}, [getGridInstance])
const { extraFilters, setExtraFilters } = useExtraFilters({
gridDto,
listFormCode,
searchParams,
onFilterChanged: refreshGrid,
})
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, refreshLookupsForRow } = useListFormColumns({
gridDto,
listFormCode,
isSubForm,
gridRef,
onShowNote: openNotePanel,
})
/* =======================
TOOLBAR
======================= */
const updateWorkflowApprovalButtons = useCallback(
(component?: any, selectedRowsData?: Record<string, unknown>[]) => {
const grid = component ?? getGridInstance()
if (!grid) return
updateWorkflowApprovalToolbarItems(
grid,
gridDto?.gridOptions.workflowDto,
selectedRowsData ?? grid.getSelectedRowsData(),
currentUser,
)
},
[currentUser, getGridInstance, gridDto],
)
const refreshData = useCallback(() => {
const grid = getGridInstance()
Promise.resolve(grid?.refresh()).finally(() => updateWorkflowApprovalButtons(grid))
void refreshWidgets()
}, [getGridInstance, updateWorkflowApprovalButtons, refreshWidgets])
const getSelectedRowKeys = useCallback(
async () => (await getGridInstance()?.getSelectedRowKeys()) ?? [],
[getGridInstance],
)
const getSelectedRowsData = useCallback(
() => getGridInstance()?.getSelectedRowsData() ?? [],
[getGridInstance],
)
const getFilter = useCallback(() => getGridInstance()?.getCombinedFilter(), [getGridInstance])
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
getFilter,
layout: layoutTypes.grid,
})
const orderedToolbarData = useMemo(
() => orderListToolbarItems([...toolbarData, ...filterToolbarData]),
[toolbarData, filterToolbarData],
)
/* =======================
GRID EVENTS
======================= */
const onSelectionChanged = useCallback(
(data: any) => {
const grid = getGridInstance()
if (!gridDto?.gridOptions || !grid) return
// Birden fazla kayıt seçiliyse ve silme yetkisi varsa toplu silme butonunu göster.
if (gridDto.gridOptions.editingOptionDto?.allowDeleting) {
const items = grid.option('toolbar')?.items ?? []
const deleteSelectedIndex = items.findIndex(
(item: any) => item.name === 'deleteSelectedRecords',
)
if (deleteSelectedIndex >= 0) {
grid.option(
`toolbar.items[${deleteSelectedIndex}].options.visible`,
data.selectedRowsData.length > 1,
)
}
}
updateWorkflowApprovalButtons(grid, data.selectedRowsData)
// SubForm'ları besleyebilmek için seçili ilk satırı sakla.
if (data.selectedRowsData.length) {
setFormData(data.selectedRowsData[0])
}
},
[getGridInstance, gridDto, updateWorkflowApprovalButtons],
)
const onCellPrepared = useMemo(() => createConditionalCellStyleHandler(gridDto), [gridDto])
const onInitNewRow = useCallback(
(e: DataGridTypes.InitNewRowEvent<any, any>) => {
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: DataGridTypes.RowInsertingEvent<any, any>) => {
e.data = buildInsertPayload(gridDto, e.data)
},
[gridDto],
)
const onRowUpdating = useCallback(
(e: DataGridTypes.RowUpdatingEvent<any, any>) => {
if (!gridDto?.columnFormats) return
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
},
[gridDto],
)
const onEditingStart = useCallback(
(e: DataGridTypes.EditingStartEvent<any, any>) => {
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: DataGridTypes.DataErrorOccurredEvent<any, any>) => {
toast.push(
<Notification type="danger" duration={2000}>
{e.error?.message}
</Notification>,
{ placement: 'bottom-end' },
)
}, [])
const cascadeMaps = useMemo(() => createCascadeMaps(gridDto), [gridDto])
const onEditorPreparing = useCallback(
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
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 })
}
// Yalnızca düzenleme formunda yer alan alanların script'i çalıştırılır.
if (columnFormat?.editOrderNo != null && columnFormat.editorScript) {
attachEditorValueChangedScript({
editor,
editorScript: columnFormat.editorScript,
fieldName: columnFormat.fieldName,
editingFormInstanceRef,
getActiveEditingForm,
})
}
attachEditorButtonContext(editor)
},
[gridDto, cascadeMaps, mode],
)
/* =======================
COLUMNS & DATASOURCE
======================= */
// listFormCode değişiminde eski kolon/veri kaynağını temizle.
useEffect(() => {
setGridDataSource(undefined)
setColumnData(undefined)
const instance = getGridInstance()
if (instance) {
instance.option('columns', undefined)
instance.option('remoteOperations', false)
instance.option('dataSource', undefined)
instance.state(null)
}
}, [listFormCode, getGridInstance])
// Kolonlar dil değiştiğinde de yeniden üretilir.
const memoizedColumns = useMemo(() => {
if (!gridDto) return undefined
const columns = getBandedColumns()
columns?.forEach(applyPhoneGlobalFormat)
return 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, currentCulture, localizationTexts])
const memoizedDataSource = useMemo(() => {
if (!gridDto) return undefined
return createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layoutTypes.grid,
// Kolonlar henüz grid'e set edilmemiş olabilir; store instance üzerinden okur.
undefined,
)
}, [gridDto, listFormCode, searchParams, createSelectDataSource])
useEffect(() => setColumnData(memoizedColumns), [memoizedColumns])
useEffect(() => setGridDataSource(memoizedDataSource), [memoizedDataSource])
useEffect(() => {
const instance = getGridInstance()
if (columnData && instance) {
instance.option('columns', columnData)
}
}, [columnData, getGridInstance])
// Otomatik state kaydetme/yükleme kapalı; manuel akış kullanılır.
useEffect(() => {
if (!gridDto) return
getGridInstance()?.option('stateStoring', { enabled: false })
}, [gridDto, getGridInstance])
/**
* State önce yüklenir, ardından dataSource set edilir. Böylece kayıtlı
* filtre/sıralama tek bir veri çekimiyle uygulanır.
*/
useEffect(() => {
const instance = getGridInstance()
if (!gridDto || !instance || !gridDataSource || !columnData) return
const applyDataSource = (state?: any) => {
instance.option('remoteOperations', remoteOperations)
if (state) instance.state(state)
instance.option('dataSource', gridDataSource)
}
customLoadState()
.then(applyDataSource)
.catch((err) => {
console.error('State load error:', err)
applyDataSource()
})
}, [gridDto, gridDataSource, columnData, customLoadState, getGridInstance])
/* =======================
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, translate),
[gridDto, mapFormItem, mode, translate],
)
const editingFormHandlers = useMemo(
() =>
createEditingFormHandlers({
getGridDto: () => gridDto,
getGridInstance,
editingFormDataRef,
editingFormInstanceRef,
}),
[gridDto, getGridInstance],
)
const editPopupOptions = useMemo(
() =>
buildEditPopupOptions({
popupOptions: gridDto?.gridOptions.editingOptionDto?.popup,
mode,
isPopupFullScreen,
useMobileEditPopup,
baseWrapperClass: 'dx-datagrid-edit-popup',
translate,
handlers: {
onSave: () => getGridInstance()?.saveEditData(),
onCancel: () => getGridInstance()?.cancelEditData(),
onToggleFullScreen: () => setIsPopupFullScreen((current) => !current),
},
}),
[gridDto, mode, isPopupFullScreen, useMobileEditPopup, translate, getGridInstance],
)
const onExporting = useCallback(
(e: DataGridTypes.ExportingEvent) => {
e.cancel = true
const grid = getGridInstance()
if (!grid) return
void exportListView({
component: grid,
format: e.format,
fileName: listFormCode,
onError: () =>
toast.push(
<Notification type="danger" duration={2500}>
{translate('::App.Common.ExportError')}
</Notification>,
{ placement: 'bottom-end' },
),
})
},
[getGridInstance, listFormCode, translate],
)
/* =======================
RENDER
======================= */
const gridOptions = gridDto?.gridOptions
const loadingMessage = !gridDto
? 'Loading grid configuration...'
: !columnData
? 'Loading columns...'
: !gridDataSource
? 'Loading data source...'
: undefined
return (
<>
<div ref={widgetGroupRef} className={widgets.length > 0 ? 'mt-2' : ''}>
<WidgetGroup widgetGroups={widgets} />
</div>
<Container className={DX_CLASSNAMES}>
{!isSubForm && <PageTitle title={translate('::' + gridOptions?.title)} />}
{loadingMessage && (
<div className="p-4">
<Loading loading>{loadingMessage}</Loading>
</div>
)}
{gridDto && gridOptions && columnData && gridDataSource && (
<div className="p-1">
<DataGrid
ref={gridRef as any}
key={'Grid-' + listFormCode}
id={'Grid-' + listFormCode}
className="list-view-toolbar-host"
height={
gridOptions.height > 0
? gridOptions.height
: gridOptions.fullHeight
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
width={gridOptions.width || '100%'}
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}
onContentReady={(e) => updateWorkflowApprovalButtons(e.component)}
onInitNewRow={onInitNewRow}
onCellPrepared={onCellPrepared}
onRowInserting={onRowInserting}
onRowUpdating={onRowUpdating}
onEditingStart={onEditingStart}
onDataErrorOccurred={onDataErrorOccurred}
onExporting={onExporting}
onEditCanceling={() => setMode('view')}
onEditCanceled={onEditFinished}
onSaved={onEditFinished}
onRowInserted={(e) => {
startWorkflowForInsertedRow(e, gridDto, listFormCode, refreshGrid)
props.refreshData?.()
void refreshWidgets()
void refreshLookupsForRow(e.component, e.data)
}}
onRowUpdated={(e) => {
props.refreshData?.()
void refreshWidgets()
void refreshLookupsForRow(e.component, e.data)
}}
onRowRemoved={() => {
props.refreshData?.()
void refreshWidgets()
}}
onEditorPreparing={onEditorPreparing}
>
<Export
enabled={gridOptions.exportDto?.enabled}
allowExportSelectedData={gridOptions.exportDto?.allowExportSelectedData}
formats={['pdf', 'xlsx']}
/>
<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}
newRowPosition={gridOptions.editingOptionDto?.newRowPosition}
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')}
/>
<GroupPanel
visible={gridOptions.groupPanelDto?.visible}
emptyPanelText={translate('::App.GroupPanel.EmptyPanelText')}
/>
<Grouping autoExpandAll={gridOptions.groupPanelDto?.autoExpandAll} />
<Selection
mode={gridOptions.selectionDto?.mode}
allowSelectAll={gridOptions.selectionDto?.allowSelectAll}
selectAllMode={gridOptions.selectionDto?.selectAllMode}
showCheckBoxesMode={gridOptions.selectionDto?.showCheckBoxesMode}
/>
<Paging defaultPageSize={gridOptions.pageSize ?? 20} />
<Pager
visible={gridOptions.pagerOptionDto?.visible}
allowedPageSizes={gridOptions.pagerOptionDto?.allowedPageSizes
?.split(',')
.map(Number)}
showPageSizeSelector={gridOptions.pagerOptionDto?.showPageSizeSelector}
showInfo={false}
showNavigationButtons={gridOptions.pagerOptionDto?.showNavigationButtons}
displayMode={gridOptions.pagerOptionDto?.displayMode}
/>
<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}
/>
<Summary>
{gridDto.columnFormats
.filter((column) => !!column.columnTotalSummaryDto?.summaryType)
.map((column) => (
<TotalItem
key={`Total_${column.fieldName}`}
column={column.fieldName}
summaryType={column.columnTotalSummaryDto.summaryType as any}
showInColumn={column.columnTotalSummaryDto.showInColumn}
valueFormat={column.columnTotalSummaryDto.valueFormat}
displayFormat={column.columnTotalSummaryDto.displayFormat}
/>
))}
{gridDto.columnFormats
.filter((column) => !!column.columnGroupSummaryDto?.summaryType)
.map((column) => (
<GroupItemDx
key={`Group_${column.fieldName}`}
column={column.fieldName}
summaryType={column.columnGroupSummaryDto.summaryType as any}
showInColumn={column.columnGroupSummaryDto.showInColumn}
valueFormat={column.columnGroupSummaryDto.valueFormat}
displayFormat={column.columnGroupSummaryDto.displayFormat}
/>
))}
</Summary>
</DataGrid>
{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="-mt-5 p-2 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 mt-2">
<ImportDashboard gridDto={gridDto} />
</Dialog.Body>
</Dialog>
</div>
)}
<Dialog
isOpen={toolbarModalData?.open || false}
onClose={() => setToolbarModalData(undefined)}
onRequestClose={() => setToolbarModalData(undefined)}
>
{toolbarModalData?.content}
</Dialog>
<GridFilterDialogs gridRef={gridRef} listFormCode={listFormCode} {...filterData} />
{notePanelTarget && (
<NotePanel
entityName={notePanelTarget.entityName}
entityId={notePanelTarget.entityId}
isVisible
onToggle={() => setNotePanelTarget(null)}
/>
)}
</Container>
</>
)
}
export default Grid