2026-02-24 20:44:16 +00:00
|
|
|
|
import Container from '@/components/shared/Container'
|
2026-07-11 20:35:32 +00:00
|
|
|
|
import '@/utils/registerDevExtremeEditors'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { Dialog, Notification, toast } from '@/components/ui'
|
2026-08-13 08:41:48 +00:00
|
|
|
|
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import { Template } from 'devextreme-react/core/template'
|
|
|
|
|
|
import DataGrid, {
|
|
|
|
|
|
ColumnChooser,
|
|
|
|
|
|
ColumnFixing,
|
2026-07-13 08:39:13 +00:00
|
|
|
|
type DataGridRef,
|
|
|
|
|
|
type DataGridTypes,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
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'
|
2026-08-13 08:41:48 +00:00
|
|
|
|
import PageTitle from '@/components/shared/PageTitle'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import SubForms from '../form/SubForms'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import { RowMode } from '../form/types'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { GridColumnData } from './GridColumnData'
|
|
|
|
|
|
import GridFilterDialogs from './GridFilterDialogs'
|
2026-10-07 18:08:10 +00:00
|
|
|
|
import { GridBoxEditorComponent, ImageUploadEditorComponent, ImageViewerEditorComponent, TagBoxEditorComponent } from './editors/cellEditors'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useFilters } from './useFilters'
|
2026-05-23 21:12:01 +00:00
|
|
|
|
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
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'
|
2026-06-18 11:23:15 +00:00
|
|
|
|
import { orderListToolbarItems } from './toolbarOrder'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { Loading } from '@/components/shared'
|
|
|
|
|
|
import { useStoreState } from '@/store'
|
2026-06-06 22:22:35 +00:00
|
|
|
|
import { NotePanel } from '../form/notes/NotePanel'
|
2026-07-10 09:22:54 +00:00
|
|
|
|
import { useListFormStateStoring } from './useListFormStateStoring'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import { exportListView } from './shared/exportListView'
|
2026-08-14 11:32:31 +00:00
|
|
|
|
import { buildEditPopupOptions, shouldUseMobileEditPopup } from './shared/editPopup'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import {
|
|
|
|
|
|
applyNewRowDefaults,
|
|
|
|
|
|
buildEditingFormItems,
|
|
|
|
|
|
buildInsertPayload,
|
|
|
|
|
|
buildUpdatePayload,
|
|
|
|
|
|
createFormItemMapper,
|
|
|
|
|
|
expandJsonFormFields,
|
|
|
|
|
|
getValueByField,
|
|
|
|
|
|
} from './shared/editingForm'
|
|
|
|
|
|
import {
|
2026-10-07 18:08:10 +00:00
|
|
|
|
createCustomEditorCellAdapter,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
createEditingFormHandlers,
|
2026-10-07 18:08:10 +00:00
|
|
|
|
prepareEditingEditor,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
} from './shared/editingFormScripts'
|
2026-10-07 18:08:10 +00:00
|
|
|
|
import { createCascadeMaps } from './shared/cascade'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import { createConditionalCellStyleHandler } from './shared/cellStyles'
|
|
|
|
|
|
import {
|
|
|
|
|
|
useExtraFilters,
|
|
|
|
|
|
useListFormCustomSources,
|
|
|
|
|
|
useListFormGridDto,
|
2026-09-30 13:45:44 +00:00
|
|
|
|
useListFormWidgets,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
useWidgetGroupHeight,
|
|
|
|
|
|
} from './shared/hooks'
|
|
|
|
|
|
import { startWorkflowForInsertedRow } from './shared/workflowRows'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
interface GridProps {
|
|
|
|
|
|
listFormCode: string
|
|
|
|
|
|
searchParams?: URLSearchParams
|
|
|
|
|
|
isSubForm?: boolean
|
|
|
|
|
|
level?: number
|
|
|
|
|
|
refreshData?: () => Promise<void>
|
|
|
|
|
|
gridDto?: GridDto
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const remoteOperations = {
|
|
|
|
|
|
paging: true,
|
|
|
|
|
|
filtering: true,
|
|
|
|
|
|
sorting: true,
|
|
|
|
|
|
grouping: false,
|
|
|
|
|
|
summary: false,
|
2026-06-26 19:27:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const Grid = (props: GridProps) => {
|
|
|
|
|
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
|
|
|
|
|
const { translate } = useLocalization()
|
2026-06-06 18:31:03 +00:00
|
|
|
|
const currentUser = useStoreState((state) => state.auth.user)
|
2026-06-29 09:37:52 +00:00
|
|
|
|
const currentCulture = useStoreState(
|
|
|
|
|
|
(state) => state.abpConfig.config?.localization.currentCulture.cultureName,
|
|
|
|
|
|
)
|
|
|
|
|
|
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const useMobileEditPopup = shouldUseMobileEditPopup()
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-13 08:41:48 +00:00
|
|
|
|
const gridRef = useRef<DataGridRef | undefined>(undefined)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
2026-05-31 09:36:51 +00:00
|
|
|
|
const editingFormDataRef = useRef<Record<string, any>>({})
|
2026-08-13 08:41:48 +00:00
|
|
|
|
const editingFormInstanceRef = useRef<any | undefined>(undefined)
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// Edit popup açıkken grid state'inin kaydedilmesini engeller.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
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')
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const gridDto = useListFormGridDto(listFormCode, extGridDto)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
2026-06-06 22:22:35 +00:00
|
|
|
|
const [notePanelTarget, setNotePanelTarget] = useState<{
|
|
|
|
|
|
entityName: string
|
|
|
|
|
|
entityId: string
|
|
|
|
|
|
} | null>(null)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-09-30 20:52:33 +00:00
|
|
|
|
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
2026-09-30 13:45:44 +00:00
|
|
|
|
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
2026-08-06 22:54:50 +00:00
|
|
|
|
useListFormCustomSources(gridDto)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const getGridInstance = useCallback(() => gridRef.current?.instance(), [])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-07-10 09:22:54 +00:00
|
|
|
|
const { customSaveState, customLoadState } = useListFormStateStoring({
|
|
|
|
|
|
listFormCode,
|
2026-08-05 20:51:43 +00:00
|
|
|
|
storageKey: gridDto?.gridOptions?.stateStoringDto?.storageKey,
|
2026-07-10 09:22:54 +00:00
|
|
|
|
filterPrefix: 'list',
|
|
|
|
|
|
skipSaveRef: isEditingRef,
|
|
|
|
|
|
})
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const { filterToolbarData, ...filterData } = useFilters({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
gridRef,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
saveGridState: customSaveState,
|
2026-08-14 14:03:34 +00:00
|
|
|
|
searchParams,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
2026-06-06 22:22:35 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const refreshGrid = useCallback(() => {
|
|
|
|
|
|
getGridInstance()?.refresh()
|
|
|
|
|
|
}, [getGridInstance])
|
|
|
|
|
|
|
|
|
|
|
|
const { extraFilters, setExtraFilters } = useExtraFilters({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
searchParams,
|
|
|
|
|
|
onFilterChanged: refreshGrid,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-06-06 22:22:35 +00:00
|
|
|
|
const openNotePanel = useCallback(
|
|
|
|
|
|
(rowData: Record<string, any>) => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const entityId = getValueByField(rowData, gridDto?.gridOptions.keyFieldName)
|
2026-06-06 22:22:35 +00:00
|
|
|
|
if (entityId === undefined || entityId === null || entityId === '') {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setNotePanelTarget({
|
|
|
|
|
|
entityName: gridDto?.gridOptions.listFormCode ?? listFormCode,
|
|
|
|
|
|
entityId: String(entityId),
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
[gridDto, listFormCode],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-30 13:45:44 +00:00
|
|
|
|
const { getBandedColumns, refreshLookupsForRow } = useListFormColumns({
|
2026-02-24 20:44:16 +00:00
|
|
|
|
gridDto,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
isSubForm,
|
|
|
|
|
|
gridRef,
|
2026-06-06 22:22:35 +00:00
|
|
|
|
onShowNote: openNotePanel,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
/* =======================
|
|
|
|
|
|
TOOLBAR
|
|
|
|
|
|
======================= */
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-05-23 21:12:01 +00:00
|
|
|
|
const updateWorkflowApprovalButtons = useCallback(
|
|
|
|
|
|
(component?: any, selectedRowsData?: Record<string, unknown>[]) => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const grid = component ?? getGridInstance()
|
|
|
|
|
|
if (!grid) return
|
2026-05-23 21:12:01 +00:00
|
|
|
|
|
|
|
|
|
|
updateWorkflowApprovalToolbarItems(
|
2026-08-06 22:54:50 +00:00
|
|
|
|
grid,
|
2026-05-23 21:12:01 +00:00
|
|
|
|
gridDto?.gridOptions.workflowDto,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
selectedRowsData ?? grid.getSelectedRowsData(),
|
2026-06-06 18:31:03 +00:00
|
|
|
|
currentUser,
|
2026-05-23 21:12:01 +00:00
|
|
|
|
)
|
|
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[currentUser, getGridInstance, gridDto],
|
2026-05-23 21:12:01 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const refreshData = useCallback(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const grid = getGridInstance()
|
|
|
|
|
|
Promise.resolve(grid?.refresh()).finally(() => updateWorkflowApprovalButtons(grid))
|
2026-09-30 13:45:44 +00:00
|
|
|
|
void refreshWidgets()
|
|
|
|
|
|
}, [getGridInstance, updateWorkflowApprovalButtons, refreshWidgets])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const getSelectedRowKeys = useCallback(
|
|
|
|
|
|
async () => (await getGridInstance()?.getSelectedRowKeys()) ?? [],
|
|
|
|
|
|
[getGridInstance],
|
|
|
|
|
|
)
|
|
|
|
|
|
const getSelectedRowsData = useCallback(
|
|
|
|
|
|
() => getGridInstance()?.getSelectedRowsData() ?? [],
|
|
|
|
|
|
[getGridInstance],
|
|
|
|
|
|
)
|
|
|
|
|
|
const getFilter = useCallback(() => getGridInstance()?.getCombinedFilter(), [getGridInstance])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
getSelectedRowKeys,
|
|
|
|
|
|
getSelectedRowsData,
|
|
|
|
|
|
refreshData,
|
|
|
|
|
|
getFilter,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
layout: layoutTypes.grid,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
})
|
2026-08-06 22:54:50 +00:00
|
|
|
|
|
2026-06-18 11:23:15 +00:00
|
|
|
|
const orderedToolbarData = useMemo(
|
|
|
|
|
|
() => orderListToolbarItems([...toolbarData, ...filterToolbarData]),
|
|
|
|
|
|
[toolbarData, filterToolbarData],
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
/* =======================
|
|
|
|
|
|
GRID EVENTS
|
|
|
|
|
|
======================= */
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const onSelectionChanged = useCallback(
|
|
|
|
|
|
(data: any) => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
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',
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
if (deleteSelectedIndex >= 0) {
|
|
|
|
|
|
grid.option(
|
|
|
|
|
|
`toolbar.items[${deleteSelectedIndex}].options.visible`,
|
|
|
|
|
|
data.selectedRowsData.length > 1,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
updateWorkflowApprovalButtons(grid, data.selectedRowsData)
|
2026-05-23 21:12:01 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// SubForm'ları besleyebilmek için seçili ilk satırı sakla.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
if (data.selectedRowsData.length) {
|
|
|
|
|
|
setFormData(data.selectedRowsData[0])
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[getGridInstance, gridDto, updateWorkflowApprovalButtons],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const onCellPrepared = useMemo(() => createConditionalCellStyleHandler(gridDto), [gridDto])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const onInitNewRow = useCallback(
|
2026-08-06 22:54:50 +00:00
|
|
|
|
(e: DataGridTypes.InitNewRowEvent<any, any>) => {
|
2026-02-24 20:44:16 +00:00
|
|
|
|
if (!gridDto?.columnFormats) {
|
2026-05-31 09:36:51 +00:00
|
|
|
|
editingFormDataRef.current = { ...(e.data || {}) }
|
2026-02-24 20:44:16 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setMode('new')
|
2026-06-11 11:38:48 +00:00
|
|
|
|
setIsPopupFullScreen(
|
2026-08-06 22:54:50 +00:00
|
|
|
|
useMobileEditPopup || (gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
|
2026-06-11 11:38:48 +00:00
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
e.promise = applyNewRowDefaults(
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
e.data,
|
|
|
|
|
|
searchParams?.get('filter'),
|
|
|
|
|
|
extraFilters,
|
|
|
|
|
|
).then((data) => {
|
|
|
|
|
|
editingFormDataRef.current = { ...data }
|
|
|
|
|
|
})
|
2026-02-24 20:44:16 +00:00
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[gridDto, searchParams, extraFilters, useMobileEditPopup],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-23 21:12:01 +00:00
|
|
|
|
const onRowInserting = useCallback(
|
|
|
|
|
|
(e: DataGridTypes.RowInsertingEvent<any, any>) => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
e.data = buildInsertPayload(gridDto, e.data)
|
2026-05-23 21:12:01 +00:00
|
|
|
|
},
|
|
|
|
|
|
[gridDto],
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const onRowUpdating = useCallback(
|
|
|
|
|
|
(e: DataGridTypes.RowUpdatingEvent<any, any>) => {
|
2026-05-17 18:20:44 +00:00
|
|
|
|
if (!gridDto?.columnFormats) return
|
2026-08-06 22:54:50 +00:00
|
|
|
|
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
},
|
|
|
|
|
|
[gridDto],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const onEditingStart = useCallback(
|
|
|
|
|
|
(e: DataGridTypes.EditingStartEvent<any, any>) => {
|
|
|
|
|
|
isEditingRef.current = true
|
2026-05-31 09:36:51 +00:00
|
|
|
|
editingFormDataRef.current = { ...(e.data || {}) }
|
2026-02-24 20:44:16 +00:00
|
|
|
|
setMode('edit')
|
2026-06-11 11:38:48 +00:00
|
|
|
|
setIsPopupFullScreen(
|
|
|
|
|
|
useMobileEditPopup || (gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
|
|
|
|
|
|
)
|
2026-05-23 21:12:01 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
expandJsonFormFields(e.component.option('columns') as GridColumnData[], e.data)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[gridDto, useMobileEditPopup],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const onEditFinished = useCallback(() => {
|
|
|
|
|
|
isEditingRef.current = false
|
|
|
|
|
|
setMode('view')
|
|
|
|
|
|
setIsPopupFullScreen(false)
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const onDataErrorOccurred = useCallback((e: DataGridTypes.DataErrorOccurredEvent<any, any>) => {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
{e.error?.message}
|
|
|
|
|
|
</Notification>,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
{ placement: 'bottom-end' },
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const cascadeMaps = useMemo(() => createCascadeMaps(gridDto), [gridDto])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const onEditorPreparing = useCallback(
|
|
|
|
|
|
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
|
2026-10-07 18:08:10 +00:00
|
|
|
|
if (!gridDto) return
|
|
|
|
|
|
prepareEditingEditor({ editor, gridDto, mode, cascadeMaps, editingFormInstanceRef })
|
2026-02-24 20:44:16 +00:00
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[gridDto, cascadeMaps, mode],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-10-07 18:08:10 +00:00
|
|
|
|
// Şablon fonksiyonları DevExtreme'de bir kez bağlanır; mod ref üzerinden okunur.
|
|
|
|
|
|
const modeRef = useRef(mode)
|
|
|
|
|
|
modeRef.current = mode
|
|
|
|
|
|
|
|
|
|
|
|
const adaptCustomEditorCell = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
createCustomEditorCellAdapter({
|
|
|
|
|
|
getGridDto: () => gridDto,
|
|
|
|
|
|
getMode: () => modeRef.current,
|
|
|
|
|
|
cascadeMaps,
|
|
|
|
|
|
editingFormInstanceRef,
|
|
|
|
|
|
}),
|
|
|
|
|
|
[gridDto, cascadeMaps],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
/* =======================
|
|
|
|
|
|
COLUMNS & DATASOURCE
|
|
|
|
|
|
======================= */
|
|
|
|
|
|
|
|
|
|
|
|
// listFormCode değişiminde eski kolon/veri kaynağını temizle.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
useEffect(() => {
|
2026-02-25 11:57:25 +00:00
|
|
|
|
setGridDataSource(undefined)
|
|
|
|
|
|
setColumnData(undefined)
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const instance = getGridInstance()
|
|
|
|
|
|
if (instance) {
|
|
|
|
|
|
instance.option('columns', undefined)
|
|
|
|
|
|
instance.option('remoteOperations', false)
|
|
|
|
|
|
instance.option('dataSource', undefined)
|
|
|
|
|
|
instance.state(null)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
}, [listFormCode, getGridInstance])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// Kolonlar dil değiştiğinde de yeniden üretilir.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const memoizedColumns = useMemo(() => {
|
2026-06-06 18:31:03 +00:00
|
|
|
|
if (!gridDto) return undefined
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-10-06 12:36:01 +00:00
|
|
|
|
return getBandedColumns()
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// getBandedColumns her render'da yeni referans üretebildiği için bağımlılığa alınmaz.
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2026-06-29 09:37:52 +00:00
|
|
|
|
}, [gridDto, currentCulture, localizationTexts])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const memoizedDataSource = useMemo(() => {
|
|
|
|
|
|
if (!gridDto) return undefined
|
|
|
|
|
|
|
|
|
|
|
|
return createSelectDataSource(
|
|
|
|
|
|
gridDto.gridOptions,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
searchParams,
|
|
|
|
|
|
layoutTypes.grid,
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// Kolonlar henüz grid'e set edilmemiş olabilir; store instance üzerinden okur.
|
|
|
|
|
|
undefined,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
2026-08-06 22:54:50 +00:00
|
|
|
|
}, [gridDto, listFormCode, searchParams, createSelectDataSource])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
useEffect(() => setColumnData(memoizedColumns), [memoizedColumns])
|
|
|
|
|
|
useEffect(() => setGridDataSource(memoizedDataSource), [memoizedDataSource])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const instance = getGridInstance()
|
|
|
|
|
|
if (columnData && instance) {
|
2026-02-24 20:44:16 +00:00
|
|
|
|
instance.option('columns', columnData)
|
|
|
|
|
|
}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
}, [columnData, getGridInstance])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// Otomatik state kaydetme/yükleme kapalı; manuel akış kullanılır.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
useEffect(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2026-02-24 20:44:16 +00:00
|
|
|
|
useEffect(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const instance = getGridInstance()
|
|
|
|
|
|
if (!gridDto || !instance || !gridDataSource || !columnData) return
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const applyDataSource = (state?: any) => {
|
|
|
|
|
|
instance.option('remoteOperations', remoteOperations)
|
|
|
|
|
|
if (state) instance.state(state)
|
|
|
|
|
|
instance.option('dataSource', gridDataSource)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
2026-02-25 11:57:25 +00:00
|
|
|
|
|
|
|
|
|
|
customLoadState()
|
2026-08-06 22:54:50 +00:00
|
|
|
|
.then(applyDataSource)
|
2026-02-25 11:57:25 +00:00
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
console.error('State load error:', err)
|
2026-08-06 22:54:50 +00:00
|
|
|
|
applyDataSource()
|
2026-02-24 20:44:16 +00:00
|
|
|
|
})
|
2026-08-06 22:54:50 +00:00
|
|
|
|
}, [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],
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const editingFormItems = useMemo(
|
2026-10-01 08:50:16 +00:00
|
|
|
|
() => buildEditingFormItems(gridDto, mapFormItem, mode, translate),
|
|
|
|
|
|
[gridDto, mapFormItem, mode, translate],
|
2026-08-06 22:54:50 +00:00
|
|
|
|
)
|
2026-06-10 14:56:00 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const editingFormHandlers = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
createEditingFormHandlers({
|
|
|
|
|
|
getGridDto: () => gridDto,
|
|
|
|
|
|
getGridInstance,
|
|
|
|
|
|
editingFormDataRef,
|
|
|
|
|
|
editingFormInstanceRef,
|
|
|
|
|
|
}),
|
|
|
|
|
|
[gridDto, getGridInstance],
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const editPopupOptions = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
buildEditPopupOptions({
|
|
|
|
|
|
popupOptions: gridDto?.gridOptions.editingOptionDto?.popup,
|
|
|
|
|
|
mode,
|
|
|
|
|
|
isPopupFullScreen,
|
|
|
|
|
|
useMobileEditPopup,
|
2026-08-19 14:58:24 +00:00
|
|
|
|
baseWrapperClass: 'dx-datagrid-edit-popup',
|
2026-08-06 22:54:50 +00:00
|
|
|
|
translate,
|
|
|
|
|
|
handlers: {
|
|
|
|
|
|
onSave: () => getGridInstance()?.saveEditData(),
|
|
|
|
|
|
onCancel: () => getGridInstance()?.cancelEditData(),
|
|
|
|
|
|
onToggleFullScreen: () => setIsPopupFullScreen((current) => !current),
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
[gridDto, mode, isPopupFullScreen, useMobileEditPopup, translate, getGridInstance],
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
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}>
|
2026-08-14 11:32:31 +00:00
|
|
|
|
{translate('::App.Common.ExportError')}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
</Notification>,
|
|
|
|
|
|
{ placement: 'bottom-end' },
|
|
|
|
|
|
),
|
|
|
|
|
|
})
|
2026-02-24 20:44:16 +00:00
|
|
|
|
},
|
2026-08-06 22:54:50 +00:00
|
|
|
|
[getGridInstance, listFormCode, translate],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
/* =======================
|
|
|
|
|
|
RENDER
|
|
|
|
|
|
======================= */
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const gridOptions = gridDto?.gridOptions
|
|
|
|
|
|
const loadingMessage = !gridDto
|
|
|
|
|
|
? 'Loading grid configuration...'
|
|
|
|
|
|
: !columnData
|
|
|
|
|
|
? 'Loading columns...'
|
|
|
|
|
|
: !gridDataSource
|
|
|
|
|
|
? 'Loading data source...'
|
|
|
|
|
|
: undefined
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
2026-09-30 13:45:44 +00:00
|
|
|
|
<div ref={widgetGroupRef} className={widgets.length > 0 ? 'mt-2' : ''}>
|
|
|
|
|
|
<WidgetGroup widgetGroups={widgets} />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<Container className={DX_CLASSNAMES}>
|
2026-08-14 11:32:31 +00:00
|
|
|
|
{!isSubForm && <PageTitle title={translate('::' + gridOptions?.title)} />}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
{loadingMessage && (
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="p-4">
|
2026-08-06 22:54:50 +00:00
|
|
|
|
<Loading loading>{loadingMessage}</Loading>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
{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?.()
|
2026-09-30 13:45:44 +00:00
|
|
|
|
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()
|
2026-08-06 22:54:50 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-10-07 18:08:10 +00:00
|
|
|
|
<Template
|
|
|
|
|
|
name="cellEditTagBox"
|
|
|
|
|
|
render={(cell: any) => TagBoxEditorComponent(adaptCustomEditorCell(cell))}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Template
|
|
|
|
|
|
name="cellEditGridBox"
|
|
|
|
|
|
render={(cell: any) => GridBoxEditorComponent(adaptCustomEditorCell(cell))}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Template
|
|
|
|
|
|
name="cellEditImageUpload"
|
|
|
|
|
|
render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))}
|
|
|
|
|
|
/>
|
2026-08-06 22:54:50 +00:00
|
|
|
|
<Template
|
|
|
|
|
|
name="cellEditImageViewer"
|
|
|
|
|
|
render={(data: any) => (
|
|
|
|
|
|
<ImageViewerEditorComponent
|
|
|
|
|
|
{...data}
|
|
|
|
|
|
fallbackFormData={editingFormDataRef.current}
|
2026-06-10 14:56:00 +00:00
|
|
|
|
/>
|
2026-08-06 22:54:50 +00:00
|
|
|
|
)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<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}
|
2026-08-14 14:03:34 +00:00
|
|
|
|
placeholder={translate('::App.Platform.Search2')}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
/>
|
|
|
|
|
|
<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}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
2026-08-06 22:54:50 +00:00
|
|
|
|
))}
|
|
|
|
|
|
</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">
|
2026-08-14 14:03:34 +00:00
|
|
|
|
{translate('::App.ListForm.ImportManager')}
|
2026-08-06 22:54:50 +00:00
|
|
|
|
</h2>
|
|
|
|
|
|
</Dialog.Header>
|
|
|
|
|
|
<Dialog.Body className="flex flex-col mt-2">
|
|
|
|
|
|
<ImportDashboard gridDto={gridDto} />
|
|
|
|
|
|
</Dialog.Body>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
isOpen={toolbarModalData?.open || false}
|
|
|
|
|
|
onClose={() => setToolbarModalData(undefined)}
|
|
|
|
|
|
onRequestClose={() => setToolbarModalData(undefined)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{toolbarModalData?.content}
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
<GridFilterDialogs gridRef={gridRef} listFormCode={listFormCode} {...filterData} />
|
2026-06-06 22:22:35 +00:00
|
|
|
|
{notePanelTarget && (
|
|
|
|
|
|
<NotePanel
|
|
|
|
|
|
entityName={notePanelTarget.entityName}
|
|
|
|
|
|
entityId={notePanelTarget.entityId}
|
|
|
|
|
|
isVisible
|
|
|
|
|
|
onToggle={() => setNotePanelTarget(null)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</Container>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Grid
|