erp-platform/ui/src/shared/useListFormColumns.ts
2025-08-19 18:00:48 +03:00

564 lines
18 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 { DataGridTypes, IFormatProps } from 'devextreme-react/data-grid'
import { DataType, HorizontalEdge, SortOrder, ValidationRule } from 'devextreme/common'
import CustomStore from 'devextreme/data/custom_store'
import { SelectedFilterOperation } from 'devextreme/ui/data_grid'
import { dynamicFetch } from '../services/form.service'
import { useLocalization } from '../utils/hooks/useLocalization'
import { addCss } from '../views/list/Utils'
import { useDialogContext } from '../views/shared/DialogContext'
import { usePWA } from '../utils/hooks/usePWA'
import { usePermission } from '../utils/hooks/usePermission'
import { GridColumnData } from '../views/list/GridColumnData'
import {
ColumnFormatDto,
EditingFormItemDto,
GridDto,
PlatformEditorTypes,
UiCommandButtonPositionTypeEnum,
UiLookupDataSourceTypeEnum,
} from '../proxy/form/models'
const cellTemplateMultiValue = (
cellElement: HTMLElement,
cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>,
) => {
if (cellInfo?.value) {
const text = Array.isArray(cellInfo.value)
? cellInfo.value
.map((a: any) => {
const { lookup } = cellInfo.column
if (lookup && lookup.calculateCellValue) {
return lookup.calculateCellValue(a)
}
return ''
})
.join(', ')
: cellInfo.value
cellElement.textContent = text
cellElement.title = text
}
}
function calculateFilterExpressionMultiValue(
this: DataGridTypes.Column,
filterValue: any,
selectedFilterOperation: string | null,
): string | Array<any> {
if (filterValue) {
if (selectedFilterOperation == '=' || selectedFilterOperation === null) {
return [this.dataField, 'contains', filterValue]
} else if (selectedFilterOperation == '<>') {
return [this.dataField, 'notcontains', filterValue]
} else {
return [this.dataField, selectedFilterOperation, filterValue]
}
} else {
// filterValue null ise isblank veya isnotblank secilmistir
if (selectedFilterOperation === '=') {
return [
this.dataField,
selectedFilterOperation,
filterValue,
'or',
this.dataField,
selectedFilterOperation,
' ',
]
} else if (selectedFilterOperation === '<>') {
return [
this.dataField,
selectedFilterOperation,
filterValue,
'and',
this.dataField,
selectedFilterOperation,
' ',
]
} else {
return [this.dataField, selectedFilterOperation, filterValue]
}
}
}
const useListFormColumns = ({
gridDto,
listFormCode,
isSubForm,
}: {
gridDto?: GridDto
listFormCode: string
isSubForm?: boolean
}) => {
const dialog: any = useDialogContext()
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const lookupDataSource = (options: any, colData: any, listFormCode: string) => {
const { lookupDto } = colData
const filters = []
if (lookupDto.cascadeParentFields) {
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
filters.push([
lookupDto?.cascadeRelationField,
lookupDto?.cascadeFilterOperator,
options?.data[lookupDto?.cascadeParentField],
])
//TODO: Statik data test edilecek
} else {
const data = options?.data ?? options
for (const cascadeParentField of lookupDto.cascadeParentFields.split(',')) {
filters.push(data[cascadeParentField])
}
}
}
//UiLookupDataSourceTypeEnum :
// Data = 1 (Statik Data),
// Query = 2 (API'den geliyor fakat API query çalıştırıyor)
// WebService = 3 (API servisten geliyor)
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
return createLookupStaticDataSource(
() => JSON.parse(lookupDto?.lookupQuery),
filters.length ? filters : null,
)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) {
return createLookupQueryDataSource(listFormCode, colData.fieldName, filters)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.WebService) {
return createLookupApiDataSource(
listFormCode,
lookupDto?.lookupQuery,
filters,
colData.lookupDto?.valueExpr?.toLowerCase(),
)
} else {
return {
store: [],
}
}
}
const createLookupStaticDataSource = (
load: () => any,
filter: any = null,
key: any = 'key',
sort: any = 'name',
) => ({
store: new CustomStore({
key,
loadMode: 'raw',
load,
}),
sort,
filter,
})
const createLookupQueryDataSource = (
listFormCode?: string,
listFormFieldName?: string,
filters?: any[],
) => {
return new CustomStore({
loadMode: 'raw',
load: async (loadOptions) => {
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
try {
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
listFormCode,
listFormFieldName,
filters,
})
return response.data.map((a: any) => ({
key: a.Key,
name: a.Name,
group: a.Group,
}))
} catch (error: any) {
// toast.push(
// <Notification type="danger" duration={2000}>
// Select error
// {error.toString()}
// </Notification>,
// {
// placement: 'top-center',
// },
// )
return null
}
},
})
}
const createLookupApiDataSource = (
listFormCode?: string,
lookupQuery?: string,
filters?: any[],
keyName?: string,
) => {
return new CustomStore({
key: keyName,
loadMode: 'raw',
load: async (loadOptions) => {
//Sayfa degismisse istek yapma
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
if (!lookupQuery) {
return
}
const [method, url, body, keySelector, nameSelector, groupSelector] = lookupQuery.split(';')
//POST;https://tcmb.gov.tr/api/doviz;{"dovizType":@param0};a=>a.DovizId;a=>a.DovizName;a=>a.Ulke
//TODO: Body dinamik params test edilecek
if (filters?.length) {
for (let i = 0; i < filters.length; i++) {
body.replace(`@param${i}`, filters[i])
}
}
try {
const response = await dynamicFetch(url, method, null, body)
let { data } = response
if (!data) {
// toast.push(
// <Notification type="danger" duration={2000}>
// {'Lookup Datası boş geldi.'}
// </Notification>,
// {
// placement: 'top-center',
// },
// )
return
}
if (!Array.isArray(data)) {
data = [data]
}
return data.map((a: any) => ({
key: eval(keySelector),
name: eval(nameSelector),
group: eval(groupSelector),
}))
} catch {
// toast.push(
// <Notification type="danger" duration={2000}>
// {'Network error'}
// </Notification>,
// {
// placement: 'top-center',
// },
// )
return
}
},
})
}
const getCommandColumn = (): GridColumnData | undefined => {
if (!gridDto) {
return
}
const hasUpdate =
gridDto.gridOptions.editingOptionDto.allowUpdating &&
checkPermission(gridDto.gridOptions.permissionDto.u)
const hasDelete =
gridDto.gridOptions.editingOptionDto.allowDeleting &&
checkPermission(gridDto.gridOptions.permissionDto.d)
const hasCommandButtons = gridDto.gridOptions.commandColumnDto.length > 0
// Eğer hiçbir buton eklenecek durumda değilse: çık
if (!hasUpdate && !hasDelete && !hasCommandButtons) {
return
}
const column = {
type: 'buttons',
width: 'auto',
buttons: [] as any,
}
if (hasUpdate) {
column.buttons.push('edit')
}
if (hasDelete) {
column.buttons.push('delete')
}
gridDto.gridOptions.commandColumnDto.forEach((action) => {
if (action.buttonPosition !== UiCommandButtonPositionTypeEnum.CommandColumn) return
if (!checkPermission(action.authName)) return
const item = {
visible: true,
hint: action.hint,
icon: action.icon,
text: translate('::' + action.text),
onClick: (e: any) => {
if (typeof e.event?.preventDefault === 'function') {
e.event.preventDefault()
}
if (action.url) {
let url = action.url?.replace('@LISTFORMCODE', listFormCode)
gridDto.columnFormats.forEach((field) => {
if (field.fieldName) {
url = url.replace(`@${field.fieldName}`, e.row.data[field.fieldName])
}
})
window.open(url, isPwaMode ? '_self' : action.urlTarget)
} else if (action.dialogName) {
if (action.dialogParameters) {
const dynamicMap = JSON.parse(action.dialogParameters)
for (const [key, value] of Object.entries<string>(dynamicMap)) {
dynamicMap[key] = value.startsWith('@') ? e.row.data[value.replace('@', '')] : value
}
dialog.setConfig({
component: action.dialogName,
props: dynamicMap,
})
}
} else if (action.onClick) {
eval(action.onClick)
}
},
}
column.buttons.push(item)
})
return column as GridColumnData
}
const getColumns = (columnFormats: ColumnFormatDto[]) => {
const columns: GridColumnData[] = []
if (!gridDto || !columnFormats) {
return columns
}
columnFormats.forEach((colData) => {
if (!colData.canRead || !colData.isActive) {
return
}
const column: GridColumnData = {}
column.colData = colData // Onemli: Baska event-callback lerde kullanmak icin eklendi, colData.lookupDto?.editorTemplateType
//column.showEditorAlways = true
column.dataField = colData.fieldName
if (colData.dataType) column.dataType = colData.dataType as DataType
if (colData.captionName) column.caption = translate('::' + colData.captionName)
if (colData.width > 0) column.width = colData.width
column.visible = colData.visible
column.alignment = colData.alignment
column.format = colData.format
column.editorOptions = { ...(colData.editorOptions as IFormatProps) }
// columnCustomizationDto
column.fixed = colData.columnCustomizationDto?.fixed
column.fixedPosition = colData.columnCustomizationDto?.fixedPosition as HorizontalEdge
column.allowReordering = colData.columnCustomizationDto?.allowReordering
// sort
if (colData.sortIndex >= 0) {
column.sortIndex = colData.sortIndex
column.sortOrder = colData.sortDirection as SortOrder
}
// filterRow
column.allowFiltering = colData.columnFilterDto?.allowFiltering
column.selectedFilterOperation = colData.columnFilterDto
?.selectedFilterOperation as SelectedFilterOperation
column.filterValue = colData.columnFilterDto?.filterValue
// headerFilter
column.allowHeaderFiltering = colData.columnHeaderDto?.allowHeaderFiltering
if (column.allowHeaderFiltering == true) {
column.headerFilter = {}
column.headerFilter.allowSearch = colData.columnHeaderDto?.allowSearch
column.headerFilter.dataSource = colData.columnHeaderDto?.dataSource
}
// search
column.allowSearch = colData.allowSearch
//export
column.allowExporting = colData.canExport
// grouping
column.allowGrouping = colData.columnGroupingDto?.allowGrouping
//if (colData.columnGroupingDto?.groupIndex > 0)
// column.groupIndex = colData.columnGroupingDto?.groupIndex;
//column.autoExpandGroup = colData.columnGroupingDto?.autoExpandGroup;
// constsa dinamik olarak css verilerini ekle
if (colData.columnCssClass) {
column.cssClass = colData.columnCssClass
if (colData.columnCssValue) {
addCss(colData.columnCssValue)
}
}
column.allowEditing = colData.columnEditingDto?.allowEditing
// #region lookup ayarlari
if (colData.lookupDto?.dataSourceType) {
// UiColumnEditorTemplateTypeEnum : None:0, Table:1, TagBox:2
const allItems = gridDto.gridOptions.editingFormDto.flatMap((group) => group.items)
const formItem = allItems.find((a) => a?.dataField === colData.fieldName)
if (formItem?.editorType2 === PlatformEditorTypes.dxTagBox) {
column.extras = {
multiValue: true,
editorOptions: formItem.editorOptions,
tagBoxOptions: formItem.tagBoxOptions,
}
column.editCellTemplate = 'cellEditTagBox'
column.calculateFilterExpression = calculateFilterExpressionMultiValue
column.cellTemplate = cellTemplateMultiValue
} else if (formItem?.editorType2 === PlatformEditorTypes.dxGridBox) {
column.extras = {
multiValue: false,
editorOptions: formItem.editorOptions,
gridBoxOptions: formItem.gridBoxOptions,
}
column.editCellTemplate = 'cellEditGridBox'
column.cellTemplate = cellTemplateMultiValue
if (formItem.gridBoxOptions?.selectionMode === 'multiple') {
column.calculateFilterExpression = calculateFilterExpressionMultiValue
column.extras.multiValue = true
}
}
column.lookup = {
valueExpr: colData.lookupDto?.valueExpr?.toLowerCase(),
displayExpr: colData.lookupDto?.displayExpr?.toLowerCase(),
dataSource: (o) => lookupDataSource(o, colData, listFormCode),
}
//column.lookup.dataSource = lookupDataSource(null, colData)
//cascadeEmptyFields verisi dolu ise bu kolon/field bir parent field dir
if (colData.lookupDto.cascadeEmptyFields) {
// parent field guncellendigi zaman bu fonksiyon cagrilir
column.setCellValue = function (rowData: any, value: any) {
if (!colData.fieldName) return
//console.log({ rowData, value, colData })
rowData[colData.fieldName] = Array.isArray(value) ? value[0] : value
// cascadeEmptyFields alani aralarinda virgul olacak sekilde bosaltilmak istenen alanlari saklar
colData?.lookupDto?.cascadeEmptyFields?.split(',').forEach((emptyField: any) => {
rowData[emptyField] = null
})
}
}
}
// #endregion
if (colData.validationRuleDto) {
// for server side validation : https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/DataValidation/jQuery/Light/
column.validationRules = colData.validationRuleDto as ValidationRule[]
}
columns.push(column)
})
return columns
}
const getBandedColumns = () => {
if (!gridDto) {
return
}
const columns: GridColumnData[] = []
const insertedColumns: (string | undefined)[] = []
const commandColumn = getCommandColumn()
if (commandColumn) {
columns.push(commandColumn)
}
for (const col of gridDto.columnFormats) {
if (!col.fieldName) {
return
}
if (insertedColumns.some((a) => a === col.fieldName)) {
// kolon zaten eklenmis ise islem yapma
return
}
if (col.bandName) {
// banded kolon ise; en fazla iki kirilima kadar eklenir
const bands = col.bandName.split(':') // ic ice banded kolon const ise aralarinda : kullanilir
if (bands.length > 1) {
// band + band + column seklinde ise :
let topBand = columns.find((e) => e.caption == bands[0] && e.isBand == true)
if (!topBand) {
// en ustteki band ilk defa ekleniyor ise
topBand = { caption: bands[0], columns: [] as GridColumnData[], isBand: true }
columns.push(topBand)
}
topBand.columns ??= [] as GridColumnData[]
const band2 = { caption: bands[1], columns: [] as GridColumnData[], isBand: true }
topBand.columns.push(band2)
const rData = gridDto.columnFormats.filter((e) => e.bandName == col.bandName)
const cols = getColumns(rData) as GridColumnData[]
band2.columns.push(...cols)
insertedColumns.push(...cols.map((e) => e.dataField))
} else {
// band + column
const band = { caption: bands[0], columns: [] as GridColumnData[], isBand: true }
const rData = gridDto.columnFormats.filter((e) => e.bandName == bands[0])
const cols = getColumns(rData) as GridColumnData[]
band.columns.push(...cols)
columns.push(band)
insertedColumns.push(...cols.map((e) => e.dataField))
}
} else {
// band a bagli olmayan kolonlar
const cols = getColumns([col])
//console.log({ col, cols: JSON.stringify(cols) })
columns.push(...cols)
insertedColumns.push(...cols.map((e) => e.dataField))
}
}
// FormEditingExtraItem
// Devexpress Gridde kaydete basılınca
// formda gözükecek olan EditingFormDto.Items elemanları gelmiyor
// Sadece grid'de tanımlanmış columnları getiriyor.
// Bu elemanları da getirmesi için aşağıdaki şekilde,
// columns'da olmayan alanları da gizli olarak ekliyoruz
if (columns?.length) {
gridDto.gridOptions.editingFormDto.forEach((group) => {
group.items?.forEach((item: EditingFormItemDto) => {
if (!columns.some((a) => a.dataField === item.dataField)) {
columns.push({
dataField: item.dataField,
visible: false,
showInColumnChooser: false,
})
insertedColumns.push(item.dataField)
}
})
})
}
return columns
}
return {
getBandedColumns,
}
}
export { useListFormColumns }