From 12bac78cb8bfef10aff3febd9226c6b23f732f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:32:04 +0300 Subject: [PATCH] Grid, Pivot ve Tree Exporting --- ui/public/version.json | 2 +- .../admin/listForm/edit/FormTabDetails.tsx | 664 +++++++++--------- ui/src/views/list/Grid.tsx | 66 +- ui/src/views/list/Pivot.tsx | 225 +++--- ui/src/views/list/Tree.tsx | 8 - .../views/list/useListFormCustomDataSource.ts | 78 +- 6 files changed, 533 insertions(+), 510 deletions(-) diff --git a/ui/public/version.json b/ui/public/version.json index 98f81987..63828e14 100644 --- a/ui/public/version.json +++ b/ui/public/version.json @@ -1,5 +1,5 @@ { - "commit": "270e50e0", + "commit": "809bfb71", "releases": [ { "version": "1.0.40", diff --git a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx index 39df60c4..944dd1f0 100644 --- a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx @@ -1,5 +1,5 @@ import { Container } from '@/components/shared' -import { Button, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui' +import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui' import { ListFormEditTabs } from '@/proxy/admin/list-form/options' import { LanguageInfo } from '@/proxy/config/models' import { SelectBoxOption } from '@/types/shared' @@ -63,370 +63,386 @@ function FormTabDetails( }, [languages]) return ( - - { - await props.onSubmit(ListFormEditTabs.DetailsForm, values, formikHelpers) - }} - > - {({ touched, errors, isSubmitting, values }) => ( -
- - - - {({ field, form }: FieldProps) => ( - option.value === values.listFormType, - )} - onChange={(option) => { - form.setFieldValue(field.name, option?.value) - props.onFormTypeChange(option?.value || 'List') - }} - /> - )} - - - - - - - - - - - - - - - - - - - {({ field, form }: FieldProps) => ( - option.value === values.cultureName)} + onChange={(option) => form.setFieldValue(field.name, option?.value)} + /> + )} + + + + + + {({ field, form }: FieldProps) => ( + option.value === values.layoutDto.defaultLayout, - )} - onChange={(option) => form.setFieldValue(field.name, option?.value)} - /> - )} - + name="width" + placeholder={translate('::ListForms.ListFormEdit.DetailsWidth')} + component={Input} + /> - - )} - -
+ + + + + + +
- - - - - - - - - - - - - - - - + {({ field, form }: FieldProps) => ( + option.value === values.roleId)} - onChange={(option) => form.setFieldValue(field.name, option?.value)} - /> - )} - - + {values.listFormType === 'List' && ( + <> + + + {({ field, form }: FieldProps) => ( + option.value === values.userId)} - onChange={(option) => form.setFieldValue(field.name, option?.value)} - /> - )} - - +
+ + + - - - - )} - - + + + + + + + + + + + + + + + + + + + +
+ + )} + + + + {({ field, form }: FieldProps) => ( + option.value === values.userId)} + onChange={(option) => form.setFieldValue(field.name, option?.value)} + /> + )} + + + + + + +
+ + )} +
) } diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx index d012a347..d9efddc1 100644 --- a/ui/src/views/list/Grid.tsx +++ b/ui/src/views/list/Grid.tsx @@ -29,7 +29,6 @@ import DataGrid, { GroupItem as GroupItemDx, GroupPanel, HeaderFilter, - IStateStoringProps, LoadPanel, Pager, Paging, @@ -74,7 +73,6 @@ import { useListFormCustomDataSource } from './useListFormCustomDataSource' import { useListFormColumns } from './useListFormColumns' import { Loading } from '@/components/shared' import { useStoreState } from '@/store' -import { locale, loadMessages } from 'devextreme/localization' interface GridProps { listFormCode: string @@ -108,14 +106,6 @@ const Grid = (props: GridProps) => { const [isPopupFullScreen, setIsPopupFullScreen] = useState(false) const [widgetGroupHeight, setWidgetGroupHeight] = useState(0) - const preloadExportLibs = () => { - import('exceljs') - import('file-saver') - import('devextreme/excel_exporter') - import('jspdf') - import('devextreme/pdf_exporter') - } - type EditorOptionsWithButtons = { buttons?: any[] } & Record @@ -985,9 +975,9 @@ const Grid = (props: GridProps) => { if (!grid) return try { - if (e.format === 'xlsx' || e.format === 'csv') { - // exceljs + file-saver + devextreme excel exporter => ihtiyaç anında yükle - const [{ Workbook }, { saveAs }, { exportDataGrid: exportDataExcel }] = await Promise.all([ + if (e.format === 'xlsx') { + // Sadece Excel için gerekli kütüphaneleri yükle + const [{ Workbook }, { saveAs }, { exportDataGrid }] = await Promise.all([ import('exceljs'), import('file-saver'), import('devextreme/excel_exporter'), @@ -996,25 +986,45 @@ const Grid = (props: GridProps) => { const workbook = new Workbook() const worksheet = workbook.addWorksheet(`${listFormCode}_sheet`) - await exportDataExcel({ + await exportDataGrid({ component: grid as any, worksheet, autoFilterEnabled: true, }) - if (e.format === 'xlsx') { - const buffer = await workbook.xlsx.writeBuffer() - saveAs( - new Blob([buffer], { type: 'application/octet-stream' }), - `${listFormCode}_export.xlsx`, - ) - } else { - const buffer = await workbook.csv.writeBuffer() - saveAs( - new Blob([buffer], { type: 'application/octet-stream' }), - `${listFormCode}_export.csv`, - ) - } + const buffer = await workbook.xlsx.writeBuffer() + saveAs( + new Blob([buffer], { type: 'application/octet-stream' }), + `${listFormCode}_export.xlsx`, + ) + } else if (e.format === 'csv') { + // Sadece CSV için gerekli kütüphaneleri yükle (exceljs CSV desteği için) + const [{ Workbook }, { saveAs }] = await Promise.all([ + import('exceljs'), + import('file-saver'), + ]) + + const workbook = new Workbook() + const worksheet = workbook.addWorksheet(`${listFormCode}_sheet`) + + // CSV için basit data export + const dataSource = grid.getDataSource() + const items = dataSource?.items() || [] + const columns = grid.getVisibleColumns().filter((c: any) => c.dataField) + + // Header ekle + worksheet.addRow(columns.map((c: any) => c.caption || c.dataField)) + + // Data ekle + items.forEach((item: any) => { + worksheet.addRow(columns.map((c: any) => item[c.dataField!])) + }) + + const buffer = await workbook.csv.writeBuffer() + saveAs( + new Blob([buffer], { type: 'text/csv' }), + `${listFormCode}_export.csv`, + ) } else if (e.format === 'pdf') { // jspdf + devextreme pdf exporter => ihtiyaç anında yükle const [jspdfMod, { exportDataGrid: exportDataPdf }] = await Promise.all([ @@ -1151,7 +1161,7 @@ const Grid = (props: GridProps) => { { gridRef, }) - const onCellPrepared = useCallback((e: any) => { - const columnFormats = gridDto?.columnFormats - if (!columnFormats) { - return - } + 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) + // 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]) + }, + [gridDto], + ) const clearPivotFilters = useCallback(() => { const grid = gridRef.current?.instance() @@ -181,7 +181,7 @@ const Pivot = (props: PivotProps) => { // Filtreleri temizle clearPivotFilters() setGridPanelColor('transparent') - + toast.push( {translate('::ListForms.ListForm.GridStateReset')} @@ -191,43 +191,46 @@ const Pivot = (props: PivotProps) => { } }, [listFormCode, storageKey, clearPivotFilters, translate]) - const onExporting = useCallback(async (e: PivotGridTypes.ExportingEvent) => { - e.cancel = true + const onExporting = useCallback( + async (e: PivotGridTypes.ExportingEvent) => { + e.cancel = true - const pivot = gridRef?.current?.instance() - if (!pivot) return + 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'), - ]) + 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`) + const workbook = new Workbook() + const worksheet = workbook.addWorksheet(`${listFormCode}_pivot`) - await exportPivotGrid({ - component: pivot as any, - worksheet, - }) + 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]) + 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( @@ -244,22 +247,19 @@ const Pivot = (props: PivotProps) => { [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], - ) + 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 @@ -373,16 +373,18 @@ const Pivot = (props: PivotProps) => { 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) + 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) - }) + }) + .catch((err) => { + console.error('Pivot state load error:', err) + }) } }, [gridDto, columnData, gridDataSource, customLoadState]) @@ -416,7 +418,6 @@ const Pivot = (props: PivotProps) => {
-