392 lines
15 KiB
TypeScript
392 lines
15 KiB
TypeScript
import { DataGridRef } from 'devextreme-react/data-grid'
|
||
import { PivotGridRef } from 'devextreme-react/pivot-grid'
|
||
import CustomStore from 'devextreme/data/custom_store'
|
||
import { MutableRefObject, useCallback } from 'react'
|
||
import { layoutTypes, ListViewLayoutType } from '@/views/admin/listForm/edit/types'
|
||
import { getLoadOptions, getServiceAddress, setGridPanelColor } from './Utils'
|
||
import { GridOptionsDto } from '@/proxy/form/models'
|
||
import { GridColumnData } from './GridColumnData'
|
||
import { dynamicFetch } from '@/services/form.service'
|
||
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||
import { TreeListRef } from 'devextreme-react/cjs/tree-list'
|
||
import { GanttRef } from 'devextreme-react/cjs/gantt'
|
||
import { SchedulerRef } from 'devextreme-react/cjs/scheduler'
|
||
import { CardViewRef, CardViewTypes } from 'devextreme-react/cjs/card-view'
|
||
|
||
const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk
|
||
|
||
const useListFormCustomDataSource = ({
|
||
gridRef,
|
||
}: {
|
||
gridRef:
|
||
| MutableRefObject<DataGridRef<any, any> | undefined>
|
||
| MutableRefObject<PivotGridRef | undefined>
|
||
| MutableRefObject<TreeListRef<any, any> | undefined>
|
||
| MutableRefObject<GanttRef | undefined>
|
||
| MutableRefObject<SchedulerRef | undefined>
|
||
| MutableRefObject<CardViewRef | undefined>
|
||
}) => {
|
||
const createSelectDataSource = useCallback(
|
||
(
|
||
gridOptions: GridOptionsDto,
|
||
listFormCode: string,
|
||
searchParams?: URLSearchParams,
|
||
layout?: ListViewLayoutType | string,
|
||
cols?: GridColumnData[] | CardViewTypes.Column[],
|
||
) => {
|
||
const store: any = new CustomStore({
|
||
key:
|
||
layout === layoutTypes.tree
|
||
? gridOptions.treeOptionDto?.keyExpr
|
||
: gridOptions.keyFieldName,
|
||
useDefaultSearch: true,
|
||
load: async (loadOptions) => {
|
||
// URL'den sort parametresini al ve loadOptions'a ekle
|
||
const urlSort = searchParams?.get('sort')
|
||
if (urlSort && !loadOptions.sort) {
|
||
try {
|
||
loadOptions.sort = JSON.parse(urlSort)
|
||
} catch (e) {
|
||
console.error('Sort parse error:', e)
|
||
}
|
||
}
|
||
|
||
const parameters = getLoadOptions(loadOptions, {
|
||
listFormCode,
|
||
filter: '',
|
||
createDeleteQuery: searchParams?.get('createDeleteQuery'),
|
||
chart: layout === layoutTypes.chart,
|
||
})
|
||
|
||
// Chart için group ve groupSummary parametreleri ekle
|
||
if (layout === layoutTypes.chart && gridOptions.seriesDto && gridOptions.seriesDto.length > 0) {
|
||
// Tüm series'lerin unique argumentField'larını topla
|
||
const allArgumentFields = [...new Set(gridOptions.seriesDto.map(s => s.argumentField).filter(Boolean))] as string[]
|
||
|
||
// İlk argumentField üzerinden group yap (chart tek bir X ekseni kullanır)
|
||
if (allArgumentFields.length > 0) {
|
||
loadOptions.group = allArgumentFields.map(field => ({
|
||
selector: field as string,
|
||
isExpanded: false
|
||
}))
|
||
}
|
||
|
||
// Tüm series'lerin valueField'ları için summary hesapla
|
||
const groupSummaries: any[] = []
|
||
gridOptions.seriesDto.forEach(series => {
|
||
if (series.valueField && series.summaryType) {
|
||
groupSummaries.push({
|
||
selector: series.valueField,
|
||
summaryType: series.summaryType as any
|
||
})
|
||
}
|
||
})
|
||
|
||
if (groupSummaries.length > 0) {
|
||
loadOptions.groupSummary = groupSummaries
|
||
}
|
||
|
||
// Parametreleri tekrar oluştur
|
||
const chartParameters = getLoadOptions(loadOptions, {
|
||
listFormCode,
|
||
filter: '',
|
||
createDeleteQuery: searchParams?.get('createDeleteQuery'),
|
||
chart: layout === layoutTypes.chart,
|
||
})
|
||
|
||
Object.assign(parameters, chartParameters)
|
||
}
|
||
// 1. Default filter'ı al
|
||
const defaultFilter = searchParams?.get('filter')
|
||
? JSON.parse(searchParams.get('filter')!)
|
||
: null
|
||
|
||
let combinedFilter: any = parameters.filter
|
||
|
||
// 2. Eğer hem default hem de grid filter varsa merge et
|
||
if (defaultFilter && combinedFilter) {
|
||
combinedFilter = [defaultFilter, 'and', combinedFilter]
|
||
} else if (defaultFilter) {
|
||
combinedFilter = defaultFilter
|
||
}
|
||
//editing asamasinda her bir field de yapilan degisiklik load istegi olarak buraya dusuyor.
|
||
//TODO: bu bug halen devam ediyor!!
|
||
//Bunu engellemek icin eklendi.
|
||
//if (!params.hasOwnProperty('requireTotalCount')) {
|
||
// return;
|
||
//}
|
||
try {
|
||
if (gridRef?.current) {
|
||
//TODO:
|
||
}
|
||
// Type guard to handle union type for gridRef
|
||
let columns = cols
|
||
if (gridRef?.current?.instance()) {
|
||
const instance = gridRef?.current?.instance() as any
|
||
const instanceColumns = instance.option('columns')
|
||
if (instanceColumns) {
|
||
columns = instanceColumns as GridColumnData[]
|
||
}
|
||
}
|
||
// URL'deki filtreyi grid'in mevcut filtrelerinin üzerine uygula
|
||
if (columns?.length && searchParams) {
|
||
const filters: (string[] | string)[] = []
|
||
for (const col of columns) {
|
||
if (col.dataField) {
|
||
const sValue = searchParams.get(col.dataField)
|
||
if (sValue) {
|
||
filters.push([col.dataField, '=', sValue])
|
||
filters.push('and')
|
||
}
|
||
}
|
||
}
|
||
if (filters.length) {
|
||
filters.pop() // son and'i sil
|
||
if (parameters.filter) {
|
||
parameters.filter = [...parameters.filter, 'and', ...filters]
|
||
} else {
|
||
parameters.filter = filters
|
||
}
|
||
}
|
||
}
|
||
|
||
if (combinedFilter && combinedFilter.length > 0) {
|
||
parameters.filter = JSON.stringify(combinedFilter)
|
||
} else {
|
||
delete parameters.filter // hiç göndermesin
|
||
}
|
||
|
||
//parameters.filter = JSON.stringify(parameters.filter)
|
||
const response = await dynamicFetch('list-form-select/select', 'GET', parameters)
|
||
|
||
// Chart için grouped data'yı chart formatına çevir
|
||
if (layout === layoutTypes.chart && Array.isArray(response.data.data)) {
|
||
const flattenGroupedData = (items: any[], parentKeys: any = {}): any[] => {
|
||
const result: any[] = []
|
||
|
||
items.forEach((item: any) => {
|
||
if (item.items && item.items.length > 0) {
|
||
// Alt grup var, recursive olarak işle
|
||
const currentKeys = { ...parentKeys }
|
||
|
||
// Bu level'daki key'i ilgili argumentField'a map'le
|
||
gridOptions.seriesDto?.forEach(series => {
|
||
if (series.argumentField && !currentKeys[series.argumentField]) {
|
||
currentKeys[series.argumentField] = item.key
|
||
}
|
||
})
|
||
|
||
result.push(...flattenGroupedData(item.items, currentKeys))
|
||
} else {
|
||
// Leaf node - gerçek data
|
||
const transformed: any = { ...parentKeys }
|
||
|
||
// Son level'daki key'i ekle
|
||
gridOptions.seriesDto?.forEach(series => {
|
||
if (series.argumentField && item.key !== undefined && !transformed[series.argumentField]) {
|
||
transformed[series.argumentField] = item.key
|
||
}
|
||
})
|
||
|
||
// Summary değerlerini valueField'lara map'le
|
||
if (Array.isArray(item.summary)) {
|
||
gridOptions.seriesDto?.forEach((series, index) => {
|
||
if (series.valueField && item.summary[index] !== undefined) {
|
||
transformed[series.valueField] = item.summary[index]
|
||
}
|
||
})
|
||
}
|
||
|
||
result.push(transformed)
|
||
}
|
||
})
|
||
|
||
return result
|
||
}
|
||
|
||
response.data.data = flattenGroupedData(response.data.data)
|
||
}
|
||
|
||
// Column format multiValue ise, gelen stringi array yapmaliyiz
|
||
if (columns) {
|
||
columns.forEach((col: any) => {
|
||
// Column multiValue mu?
|
||
if (col.extras?.multiValue) {
|
||
//console.log('MultiValue:', col.dataField, col.extras?.multiValue)
|
||
// Multivalue column icin header filter acildiginda gonderilen group querysi sonuclari
|
||
// "key" field ile geliyor, bunu array yapmamiz lazim, bu group query'nin,
|
||
// bu field icin olup olmadigini anlayabiliriz
|
||
const isGroupQuery = parameters?.group?.includes(col.dataField)
|
||
response.data.data?.forEach((row: any) => {
|
||
try {
|
||
if (row[col.dataField]) {
|
||
// value'yu arraye cevir
|
||
row[col.dataField] = row[col.dataField]?.split(MULTIVALUE_DELIMITER)
|
||
}
|
||
if (isGroupQuery && row.key) {
|
||
// group query sonucunda gelen key'i arraye cevir
|
||
row.key = row.key.split(MULTIVALUE_DELIMITER)
|
||
}
|
||
} catch (e) {
|
||
// toast.push(
|
||
// <Notification type="danger" duration={2000}>
|
||
// {'multiValue Error'}
|
||
// </Notification>,
|
||
// {
|
||
// placement: 'top-end',
|
||
// },
|
||
// )
|
||
console.log('multiValue Error', e)
|
||
// JSON parse sirasinde hata olursa data gosterimi devam etsin
|
||
}
|
||
})
|
||
}
|
||
})
|
||
|
||
//TODO: Bu neden var? Eğer gerekli değilse kaldırılabilir.
|
||
searchParams?.set('createDeleteQuery', 'false')
|
||
}
|
||
|
||
// gride filtre uygulanmis ise renklendir
|
||
setGridPanelColor(
|
||
response.data.queryInfos?.isAppliedGridFilter
|
||
? filteredGridPanelColor
|
||
: 'transparent',
|
||
)
|
||
|
||
const retValue = {
|
||
data: response.data.data,
|
||
totalCount: response.data.totalCount,
|
||
summary: response.data.summary,
|
||
groupCount: response.data.groupCount,
|
||
}
|
||
|
||
return retValue
|
||
} catch (error: any) {
|
||
// toast.push(
|
||
// <Notification type="danger" duration={2000}>
|
||
// Select error
|
||
// {error.toString()}
|
||
// </Notification>,
|
||
// {
|
||
// placement: 'top-end',
|
||
// },
|
||
// )
|
||
return null
|
||
}
|
||
},
|
||
// totalCount: async (loadOptions) => {
|
||
// const parameters = getLoadOptions(loadOptions, {
|
||
// listFormCode,
|
||
// filter: '',
|
||
// createDeleteQuery: searchParams?.get('createDeleteQuery'),
|
||
// group: '',
|
||
// })
|
||
|
||
// // 1. Default filter'ı al
|
||
// const defaultFilter = searchParams?.get('filter')
|
||
// ? JSON.parse(searchParams.get('filter')!)
|
||
// : null
|
||
|
||
// let combinedFilter: any = parameters.filter
|
||
|
||
// // 2. Eğer hem default hem de grid filter varsa merge et
|
||
// if (defaultFilter && combinedFilter) {
|
||
// combinedFilter = [defaultFilter, 'and', combinedFilter]
|
||
// } else if (defaultFilter) {
|
||
// combinedFilter = defaultFilter
|
||
// }
|
||
|
||
// if (combinedFilter && combinedFilter.length > 0) {
|
||
// parameters.filter = JSON.stringify(combinedFilter)
|
||
// } else {
|
||
// delete parameters.filter // hiç göndermesin
|
||
// }
|
||
|
||
// try {
|
||
// const response = await dynamicFetch('list-form-select/select', 'GET', parameters)
|
||
// return response.data.totalCount
|
||
// } catch (error: any) {
|
||
// // toast.push(
|
||
// // <Notification type="danger" duration={2000}>
|
||
// // TotalCount error
|
||
// // {error.toString()}
|
||
// // </Notification>,
|
||
// // {
|
||
// // placement: 'top-end',
|
||
// // },
|
||
// // )
|
||
// return null
|
||
// }
|
||
// },
|
||
byKey: async (key) => {
|
||
const parameters = getLoadOptions(
|
||
{ key },
|
||
{ listFormCode, filter: '', createDeleteQuery: '' },
|
||
)
|
||
|
||
parameters.filter = JSON.stringify(parameters.filter)
|
||
try {
|
||
const response = await dynamicFetch('list-form-select/select', 'GET', parameters)
|
||
return response.data.data[0]
|
||
} catch (error: any) {
|
||
// toast.push(
|
||
// <Notification type="danger" duration={2000}>
|
||
// ByKey error
|
||
// {error.toString()}
|
||
// </Notification>,
|
||
// {
|
||
// placement: 'top-end',
|
||
// },
|
||
// )
|
||
return null
|
||
}
|
||
},
|
||
remove: function (key) {
|
||
if (!gridOptions.deleteServiceAddress) {
|
||
return
|
||
}
|
||
|
||
const deleteUrl = getServiceAddress(gridOptions.deleteServiceAddress)
|
||
|
||
return dynamicFetch(deleteUrl, 'POST', searchParams, {
|
||
keys: [key],
|
||
listFormCode,
|
||
})
|
||
},
|
||
update: function (key, values) {
|
||
if (!gridOptions.updateServiceAddress) {
|
||
return
|
||
}
|
||
const updateUrl = getServiceAddress(gridOptions.updateServiceAddress)
|
||
|
||
return dynamicFetch(updateUrl, 'POST', searchParams, {
|
||
keys: [key],
|
||
data: values,
|
||
listFormCode,
|
||
})
|
||
},
|
||
insert: function (values) {
|
||
if (!gridOptions.insertServiceAddress) {
|
||
return
|
||
}
|
||
const insertUrl = getServiceAddress(gridOptions.insertServiceAddress)
|
||
|
||
return dynamicFetch(insertUrl, 'POST', searchParams, { data: values, listFormCode })
|
||
},
|
||
errorHandler: (error: any) => {
|
||
console.log(error.message)
|
||
},
|
||
})
|
||
|
||
return store
|
||
},
|
||
[],
|
||
)
|
||
|
||
return {
|
||
createSelectDataSource,
|
||
}
|
||
}
|
||
|
||
export { useListFormCustomDataSource }
|