import Container from '@/components/shared/Container' import { DX_CLASSNAMES } from '@/constants/app.constant' import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models' import { getListFormCustomization, postListFormCustomization, } from '@/services/list-form-customization.service' import { useLocalization } from '@/utils/hooks/useLocalization' import Chart, { ChartRef, CommonSeriesSettings, Size, Tooltip, } from 'devextreme-react/chart' import PivotGrid, { Export, FieldChooser, FieldPanel, HeaderFilter, LoadPanel, PivotGridRef, PivotGridTypes, Scrolling, Search, StateStoring, } from 'devextreme-react/pivot-grid' import CustomStore from 'devextreme/data/custom_store' import PivotGridDataSource, { Field } from 'devextreme/ui/pivot_grid/data_source' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Helmet } from 'react-helmet' import { GridColumnData } from './GridColumnData' import { addCss, addJs, controlStyleCondition, pivotFieldConvertDataType, setGridPanelColor, } from './Utils' import { useFilters } from './useFilters' import WidgetGroup from '@/components/common/WidgetGroup' import { Button, Notification, toast } from '@/components/ui' import { FaCog, FaSave, FaTimes, FaUndo } from 'react-icons/fa' import { usePermission } from '@/utils/hooks/usePermission' import { ROUTES_ENUM } from '@/routes/route.constant' import { usePWA } from '@/utils/hooks/usePWA' import { layoutTypes } from '../admin/listForm/edit/types' import { useListFormCustomDataSource } from './useListFormCustomDataSource' import { useListFormColumns } from './useListFormColumns' import { useStoreState } from '@/store' interface PivotProps { listFormCode: string searchParams?: URLSearchParams isSubForm?: boolean level?: number refreshData?: () => Promise gridDto?: GridDto refreshGridDto: () => Promise } const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk const Pivot = (props: PivotProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto } = props const { translate } = useLocalization() const { checkPermission } = usePermission() const isPwaMode = usePWA() const gridRef = useRef() const chartRef = useRef(null) const refListFormCode = useRef('') const config = useStoreState((state) => state.abpConfig.config) const [gridDataSource, setGridDataSource] = useState>() const [columnData, setColumnData] = useState() // StateStoring için storageKey'i memoize et const storageKey = useMemo(() => { return gridDto?.gridOptions.stateStoringDto?.storageKey ?? '' }, [gridDto?.gridOptions.stateStoringDto?.storageKey]) const { filterToolbarData, ...filterData } = useFilters({ gridDto, gridRef, listFormCode, }) const { createSelectDataSource } = useListFormCustomDataSource({ gridRef }) const { getBandedColumns } = useListFormColumns({ gridDto, listFormCode, isSubForm, gridRef, }) const onCellPrepared = useCallback((e: any) => { const columnFormats = gridDto?.columnFormats if (!columnFormats) { return } // satir, hucre yada header vb. kisimlara conditional style uygulamak icin for (let indxCol = 0; indxCol < columnFormats.length; indxCol++) { const colFormat = columnFormats[indxCol] for (let indxStyl = 0; indxStyl < colFormat.columnStylingDto.length; indxStyl++) { const colStyle = colFormat.columnStylingDto[indxStyl] // uygulanacak style if (e.rowType == colStyle.rowType) { // header, filter, data, group, summaries ..her birisine style uygulanabilir // style bütün satıra uygulansın olarak seçili ise yada sadece ilgili field üzerinde ise if (colStyle.useRow || e.column?.dataField == colFormat.fieldName) { if ( !colStyle.conditionValue || controlStyleCondition(e.data, colFormat.fieldName, colStyle) ) { // css sınıf ismi var ise uygula if (colStyle.cssClassName) { e.cellElement.addClass(colStyle.cssClassName) } // css inline style var ise uygula if (colStyle.cssStyles) { e.cellElement.attr('style', e.cellElement.attr('style') + ';' + colStyle.cssStyles) } } } } } } }, [gridDto]) const clearPivotFilters = useCallback(() => { const grid = gridRef.current?.instance() if (!grid) return const ds = grid.getDataSource() if (ds) { const fields = ds.fields() fields.forEach((f: any) => { f.filterValues = undefined f.filterType = undefined }) ds.reload() } }, []) const moveAllFieldsToFilterArea = useCallback(() => { const grid = gridRef.current?.instance() if (!grid) return const ds = grid.getDataSource() if (!ds) return const fields = ds.fields() fields.forEach((field: any) => { field.area = 'filter' // tüm alanları filtre alanına taşı field.areaIndex = undefined }) ds.fields(fields) ds.reload() // PivotGrid’i yeniden yükle grid.repaint() // UI güncelle }, []) const resetPivotGridState = useCallback(async () => { const grid = gridRef.current?.instance() if (grid) { // State'i veritabanından sil await postListFormCustomization({ listFormCode: listFormCode, customizationType: ListFormCustomizationTypeEnum.GridState, filterName: `pivot-${storageKey}`, customizationData: '', }) // DataSource'u resetle const ds = grid.getDataSource() if (ds && typeof ds.state === 'function') { ds.state(null) // State'i temizle } // Filtreleri temizle clearPivotFilters() setGridPanelColor('transparent') toast.push( {translate('::ListForms.ListForm.GridStateReset')} , { placement: 'top-end' }, ) } }, [listFormCode, storageKey, clearPivotFilters, translate]) const onExporting = useCallback(async (e: PivotGridTypes.ExportingEvent) => { e.cancel = true const pivot = gridRef?.current?.instance() if (!pivot) return try { // PivotGrid sadece Excel export destekliyor const [{ Workbook }, { saveAs }, { exportPivotGrid }] = await Promise.all([ import('exceljs'), import('file-saver'), import('devextreme/excel_exporter'), ]) const workbook = new Workbook() const worksheet = workbook.addWorksheet(`${listFormCode}_pivot`) await exportPivotGrid({ component: pivot as any, worksheet, }) const buffer = await workbook.xlsx.writeBuffer() saveAs( new Blob([buffer], { type: 'application/octet-stream' }), `${listFormCode}_pivot_export.xlsx`, ) } catch (err) { console.error('Pivot export error:', err) toast.push( {translate('::App.Common.ExportError') ?? 'Dışa aktarma sırasında hata oluştu.'} , { placement: 'top-end' }, ) } }, [listFormCode, translate]) // StateStoring fonksiyonlarını ref'e kaydet const customSaveState = useCallback( (state: any) => { return postListFormCustomization({ listFormCode: listFormCode, customizationType: ListFormCustomizationTypeEnum.GridState, filterName: `pivot-${storageKey}`, customizationData: JSON.stringify(state), }).then(() => { setGridPanelColor(statedGridPanelColor) }) }, [listFormCode, storageKey], ) const customLoadState = useCallback( () => { return getListFormCustomization( listFormCode, ListFormCustomizationTypeEnum.GridState, `pivot-${storageKey}`, ).then((response: any) => { if (response.data?.length > 0) { setGridPanelColor(statedGridPanelColor) return JSON.parse(response.data[0].customizationData) } return null }) }, [listFormCode, storageKey], ) useEffect(() => { refListFormCode.current = listFormCode }, [listFormCode]) useEffect(() => { if (!gridDto) { return } // Set js and css const grdOpt = gridDto.gridOptions if (grdOpt.customJsSources.length) { for (const js of grdOpt.customJsSources) { addJs(js) } } if (grdOpt.customStyleSources.length) { for (const css of grdOpt.customStyleSources) { addCss(css) } } }, [gridDto]) // Kolonları memoize et const memoizedColumns = useMemo(() => { if (!gridDto || !config) return undefined const cols = getBandedColumns() return cols?.filter((a) => a.colData?.pivotSettingsDto.isPivot) }, [gridDto, config]) // DataSource'u memoize et const memoizedDataSource = useMemo(() => { if (!gridDto) return undefined const cols = getBandedColumns() return createSelectDataSource( gridDto.gridOptions, listFormCode, searchParams, layoutTypes.pivot, cols, ) }, [gridDto, listFormCode, createSelectDataSource]) useEffect(() => { if (memoizedColumns) { setColumnData(memoizedColumns) } }, [memoizedColumns]) useEffect(() => { if (memoizedDataSource) { setGridDataSource(memoizedDataSource) } }, [memoizedDataSource]) // StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak useEffect(() => { if (!gridDto || !gridRef?.current) return const instance = gridRef?.current?.instance() if (instance) { instance.option('stateStoring', { enabled: false, }) } }, [gridDto]) // Pivot dataSource ve fields'i set et useEffect(() => { if (!columnData || !gridDataSource || !gridRef?.current || !gridDto) { return } const instance = gridRef?.current?.instance() if (!instance) return // Default fields'i hazırla const defaultFields: any = columnData?.map((b) => { return { dataField: b.dataField, caption: b.caption, dataType: pivotFieldConvertDataType(b.dataType), area: b.colData?.pivotSettingsDto.area, format: b.colData?.pivotSettingsDto.format, summaryType: b.colData?.pivotSettingsDto.summaryType, groupInterval: b.colData?.pivotSettingsDto.groupInterval, sortOrder: b.colData?.pivotSettingsDto.sortOrder, expanded: b.colData?.pivotSettingsDto.expanded, wordWrapEnabled: b.colData?.pivotSettingsDto.wordWrapEnabled, visible: true, width: b.width, } as Field }) // DataSource'u ayarla const dataSource: PivotGridTypes.Properties['dataSource'] = { remoteOperations: true, store: gridDataSource, fields: defaultFields, } instance.option('dataSource', dataSource) }, [columnData, gridDataSource, gridDto]) // Component mount olduğunda state'i bir kez yükle useEffect(() => { if (!gridDto || !gridRef?.current || !columnData || !gridDataSource) return const instance = gridRef?.current?.instance() if (instance) { customLoadState().then((state) => { if (state) { const ds = instance.getDataSource() if (ds && typeof ds.state === 'function') { ds.state(state) } } }).catch((err) => { console.error('Pivot state load error:', err) }) } }, [gridDto, columnData, gridDataSource, customLoadState]) // Chart binding - sadece bir kez useEffect(() => { if (!gridRef?.current || !chartRef?.current) return const pivotInstance = gridRef?.current?.instance() const chartInstance = chartRef?.current?.instance() if (pivotInstance && chartInstance) { pivotInstance.bindChart(chartInstance, { dataFieldsDisplayMode: 'splitPanes', alternateDataFields: false, }) } }, [gridDto]) return ( <> {!isSubForm && ( )} {gridDto && columnData && (
{checkPermission(gridDto?.gridOptions.permissionDto.u) && ( )}
{gridDto.gridOptions.pivotOptionDto.showChart && ( )}
)}
) } export default Pivot