Grid, Pivot ve Tree Exporting

This commit is contained in:
Sedat ÖZTÜRK 2026-02-05 11:32:04 +03:00
parent 809bfb7129
commit 12bac78cb8
6 changed files with 533 additions and 510 deletions

View file

@ -1,5 +1,5 @@
{
"commit": "270e50e0",
"commit": "809bfb71",
"releases": [
{
"version": "1.0.40",

View file

@ -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 (
<Container className="grid xl:grid-cols-2">
<Formik
initialValues={listFormValues}
validationSchema={schema}
onSubmit={async (values, formikHelpers) => {
await props.onSubmit(ListFormEditTabs.DetailsForm, values, formikHelpers)
}}
>
{({ touched, errors, isSubmitting, values }) => (
<Form>
<FormContainer size="sm">
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsCultureName')}
invalid={errors.cultureName && touched.cultureName}
errorMessage={errors.cultureName}
>
<Field
type="text"
autoComplete="off"
name="cultureName"
placeholder={translate('::ListForms.ListFormEdit.DetailsCultureName')}
>
{({ field, form }: FieldProps<LanguageInfo>) => (
<Select
field={field}
form={form}
isClearable={true}
options={langOptions}
value={langOptions?.filter((option) => option.value === values.cultureName)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ListFormType')}
invalid={errors.listFormType && touched.listFormType}
errorMessage={errors.listFormType}
>
<Field
type="text"
autoComplete="off"
name="listFormType"
placeholder={translate('::ListForms.ListFormEdit.ListFormType')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormTypeOptions}
value={listFormTypeOptions?.filter(
(option) => option.value === values.listFormType,
)}
onChange={(option) => {
form.setFieldValue(field.name, option?.value)
props.onFormTypeChange(option?.value || 'List')
}}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsTitle')}
invalid={errors.title && touched.title}
errorMessage={errors.title}
>
<Field
type="text"
autoComplete="off"
name="title"
placeholder={translate('::ListForms.ListFormEdit.DetailsTitle')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsName')}
invalid={errors.name && touched.name}
errorMessage={errors.name}
>
<Field
type="text"
autoComplete="off"
name="name"
placeholder={translate('::ListForms.ListFormEdit.DetailsName')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsDescription')}
invalid={errors.description && touched.description}
errorMessage={errors.description}
>
<Field
type="text"
autoComplete="off"
name="description"
placeholder={translate('::ListForms.ListFormEdit.DetailsDescription')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.IsSub')}
invalid={errors.isSubForm && touched.isSubForm}
errorMessage={errors.isSubForm}
>
<Field
name="isSubForm"
placeholder={translate('::ListForms.ListFormEdit.IsSub')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.SubFormsListFormType')}
invalid={errors.subFormsListFormType && touched.subFormsListFormType}
errorMessage={errors.subFormsListFormType}
>
<Field
type="text"
autoComplete="off"
name="subFormsListFormType"
placeholder={translate('::ListForms.ListFormEdit.SubFormsListFormType')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormTypeOptions}
value={listFormTypeOptions?.filter(
(option) => option.value === values.subFormsListFormType,
)}
onChange={(option) => {
form.setFieldValue(field.name, option?.value)
props.onFormTypeChange(option?.value || 'List')
}}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ShowNote')}
invalid={errors.showNote && touched.showNote}
errorMessage={errors.showNote}
>
<Field
name="showNote"
placeholder={translate('::ListForms.ListFormEdit.ShowNote')}
component={Checkbox}
/>
</FormItem>
<div className="flex gap-2">
<Formik
initialValues={listFormValues}
validationSchema={schema}
onSubmit={async (values, formikHelpers) => {
await props.onSubmit(ListFormEditTabs.DetailsForm, values, formikHelpers)
}}
>
{({ touched, errors, isSubmitting, values }) => (
<Form>
<FormContainer size="sm">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<Card className="my-2" header="General">
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsWidth')}
invalid={errors.width && touched.width}
errorMessage={errors.width}
label={translate('::ListForms.ListFormEdit.DetailsCultureName')}
invalid={errors.cultureName && touched.cultureName}
errorMessage={errors.cultureName}
>
<Field
className="w-20"
type="number"
type="text"
autoComplete="off"
name="width"
placeholder={translate('::ListForms.ListFormEdit.DetailsWidth')}
name="cultureName"
placeholder={translate('::ListForms.ListFormEdit.DetailsCultureName')}
>
{({ field, form }: FieldProps<LanguageInfo>) => (
<Select
field={field}
form={form}
isClearable={true}
options={langOptions}
value={langOptions?.filter((option) => option.value === values.cultureName)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ListFormType')}
invalid={errors.listFormType && touched.listFormType}
errorMessage={errors.listFormType}
>
<Field
type="text"
autoComplete="off"
name="listFormType"
placeholder={translate('::ListForms.ListFormEdit.ListFormType')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormTypeOptions}
value={listFormTypeOptions?.filter(
(option) => option.value === values.listFormType,
)}
onChange={(option) => {
form.setFieldValue(field.name, option?.value)
props.onFormTypeChange(option?.value || 'List')
}}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsTitle')}
invalid={errors.title && touched.title}
errorMessage={errors.title}
>
<Field
type="text"
autoComplete="off"
name="title"
placeholder={translate('::ListForms.ListFormEdit.DetailsTitle')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsHeight')}
invalid={errors.width && touched.width}
errorMessage={errors.width}
label={translate('::ListForms.ListFormEdit.DetailsName')}
invalid={errors.name && touched.name}
errorMessage={errors.name}
>
<Field
className="w-20"
type="number"
type="text"
autoComplete="off"
name="height"
placeholder={translate('::ListForms.ListFormEdit.DetailsHeight')}
name="name"
placeholder={translate('::ListForms.ListFormEdit.DetailsName')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsFullHeight')}
invalid={errors.fullHeight && touched.fullHeight}
errorMessage={errors.fullHeight}
label={translate('::ListForms.ListFormEdit.DetailsDescription')}
invalid={errors.description && touched.description}
errorMessage={errors.description}
>
<Field
name="fullHeight"
placeholder={translate('::ListForms.ListFormEdit.DetailsFullHeight')}
type="text"
autoComplete="off"
name="description"
placeholder={translate('::ListForms.ListFormEdit.DetailsDescription')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ShowNote')}
invalid={errors.showNote && touched.showNote}
errorMessage={errors.showNote}
>
<Field
name="showNote"
placeholder={translate('::ListForms.ListFormEdit.ShowNote')}
component={Checkbox}
/>
</FormItem>
</div>
{values.listFormType === 'List' && (
<>
</Card>
<Card className="my-2" header="Layout & Popup">
<div className="flex gap-2">
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.DefaultLayout')}
invalid={errors.layoutDto?.defaultLayout && touched.layoutDto?.defaultLayout}
errorMessage={errors.layoutDto?.defaultLayout}
label={translate('::ListForms.ListFormEdit.DetailsWidth')}
invalid={errors.width && touched.width}
errorMessage={errors.width}
>
<Field
type="text"
className="w-20"
type="number"
autoComplete="off"
name="layoutDto.defaultLayout"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.DefaultLayout',
)}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormDefaultLayoutOptions}
value={listFormDefaultLayoutOptions?.filter(
(option) => option.value === values.layoutDto.defaultLayout,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
name="width"
placeholder={translate('::ListForms.ListFormEdit.DetailsWidth')}
component={Input}
/>
</FormItem>
</>
)}
<div className="flex gap-2">
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsHeight')}
invalid={errors.width && touched.width}
errorMessage={errors.width}
>
<Field
className="w-20"
type="number"
autoComplete="off"
name="height"
placeholder={translate('::ListForms.ListFormEdit.DetailsHeight')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsFullHeight')}
invalid={errors.fullHeight && touched.fullHeight}
errorMessage={errors.fullHeight}
>
<Field
name="fullHeight"
placeholder={translate('::ListForms.ListFormEdit.DetailsFullHeight')}
component={Checkbox}
/>
</FormItem>
</div>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GridLayout')}
invalid={errors.layoutDto?.grid && touched.layoutDto?.grid}
errorMessage={errors.layoutDto?.grid}
label={translate('::ListForms.ListFormEdit.IsSub')}
invalid={errors.isSubForm && touched.isSubForm}
errorMessage={errors.isSubForm}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.grid"
placeholder={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GridLayout')}
name="isSubForm"
placeholder={translate('::ListForms.ListFormEdit.IsSub')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.PivotLayout')}
invalid={errors.layoutDto?.pivot && touched.layoutDto?.pivot}
errorMessage={errors.layoutDto?.pivot}
label={translate('::ListForms.ListFormEdit.SubFormsListFormType')}
invalid={errors.subFormsListFormType && touched.subFormsListFormType}
errorMessage={errors.subFormsListFormType}
>
<Field
className="w-20"
type="text"
autoComplete="off"
name="layoutDto.pivot"
placeholder={translate('::ListForms.ListFormEdit.DetailsLayoutDto.PivotLayout')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.ChartLayout')}
invalid={errors.layoutDto?.chart && touched.layoutDto?.chart}
errorMessage={errors.layoutDto?.chart}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.chart"
placeholder={translate('::ListForms.ListFormEdit.DetailsLayoutDto.ChartLayout')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.TreeLayout')}
invalid={errors.layoutDto?.tree && touched.layoutDto?.tree}
errorMessage={errors.layoutDto?.tree}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.tree"
placeholder={translate('::ListForms.ListFormEdit.DetailsLayoutDto.TreeLayout')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout')}
invalid={errors.layoutDto?.gantt && touched.layoutDto?.gantt}
errorMessage={errors.layoutDto?.gantt}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.gantt"
placeholder={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.SchedulerLayout')}
invalid={errors.layoutDto?.scheduler && touched.layoutDto?.scheduler}
errorMessage={errors.layoutDto?.scheduler}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.scheduler"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.SchedulerLayout',
name="subFormsListFormType"
placeholder={translate('::ListForms.ListFormEdit.SubFormsListFormType')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormTypeOptions}
value={listFormTypeOptions?.filter(
(option) => option.value === values.subFormsListFormType,
)}
onChange={(option) => {
form.setFieldValue(field.name, option?.value)
}}
/>
)}
component={Checkbox}
/>
</Field>
</FormItem>
</div>
<FormItem
label="Role"
invalid={errors.roleId && touched.roleId}
errorMessage={errors.roleId}
>
<Field type="text" name="roleId">
{({ field, form }: FieldProps<IdentityRoleDto>) => (
<Select
field={field}
form={form}
options={props.roleList}
isClearable={true}
value={props.roleList.filter((option) => option.value === values.roleId)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
{values.listFormType === 'List' && (
<>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.DefaultLayout')}
invalid={errors.layoutDto?.defaultLayout && touched.layoutDto?.defaultLayout}
errorMessage={errors.layoutDto?.defaultLayout}
>
<Field
type="text"
autoComplete="off"
name="layoutDto.defaultLayout"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.DefaultLayout',
)}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={listFormDefaultLayoutOptions}
value={listFormDefaultLayoutOptions?.filter(
(option) => option.value === values.layoutDto.defaultLayout,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label="User"
invalid={errors.userId && touched.userId}
errorMessage={errors.userId}
>
<Field type="text" name="userId">
{({ field, form }: FieldProps<IdentityUserDto>) => (
<Select
field={field}
form={form}
options={props.userList}
isClearable={true}
value={props.userList.filter((option) => option.value === values.userId)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<div className="flex gap-2">
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GridLayout')}
invalid={errors.layoutDto?.grid && touched.layoutDto?.grid}
errorMessage={errors.layoutDto?.grid}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.grid"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.GridLayout',
)}
component={Checkbox}
/>
</FormItem>
<Button block variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</FormContainer>
</Form>
)}
</Formik>
</Container>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.PivotLayout')}
invalid={errors.layoutDto?.pivot && touched.layoutDto?.pivot}
errorMessage={errors.layoutDto?.pivot}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.pivot"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.PivotLayout',
)}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.ChartLayout')}
invalid={errors.layoutDto?.chart && touched.layoutDto?.chart}
errorMessage={errors.layoutDto?.chart}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.chart"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.ChartLayout',
)}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.TreeLayout')}
invalid={errors.layoutDto?.tree && touched.layoutDto?.tree}
errorMessage={errors.layoutDto?.tree}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.tree"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.TreeLayout',
)}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout')}
invalid={errors.layoutDto?.gantt && touched.layoutDto?.gantt}
errorMessage={errors.layoutDto?.gantt}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.gantt"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout',
)}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.SchedulerLayout',
)}
invalid={errors.layoutDto?.scheduler && touched.layoutDto?.scheduler}
errorMessage={errors.layoutDto?.scheduler}
>
<Field
className="w-20"
autoComplete="off"
name="layoutDto.scheduler"
placeholder={translate(
'::ListForms.ListFormEdit.DetailsLayoutDto.SchedulerLayout',
)}
component={Checkbox}
/>
</FormItem>
</div>
</>
)}
<FormItem
label="Role"
invalid={errors.roleId && touched.roleId}
errorMessage={errors.roleId}
>
<Field type="text" name="roleId">
{({ field, form }: FieldProps<IdentityRoleDto>) => (
<Select
field={field}
form={form}
options={props.roleList}
isClearable={true}
value={props.roleList.filter((option) => option.value === values.roleId)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label="User"
invalid={errors.userId && touched.userId}
errorMessage={errors.userId}
>
<Field type="text" name="userId">
{({ field, form }: FieldProps<IdentityUserDto>) => (
<Select
field={field}
form={form}
options={props.userList}
isClearable={true}
value={props.userList.filter((option) => option.value === values.userId)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
</Card>
</div>
<Button block variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</FormContainer>
</Form>
)}
</Formik>
)
}

View file

@ -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<string, any>
@ -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) => {
<Export
enabled={gridDto.gridOptions.exportDto?.enabled}
allowExportSelectedData={gridDto.gridOptions.exportDto?.allowExportSelectedData}
formats={['pdf', 'xlsx', 'csv']}
formats={['pdf', 'xlsx']}
/>
<Editing
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}

View file

@ -6,12 +6,7 @@ import {
postListFormCustomization,
} from '@/services/list-form-customization.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import Chart, {
ChartRef,
CommonSeriesSettings,
Size,
Tooltip,
} from 'devextreme-react/chart'
import Chart, { ChartRef, CommonSeriesSettings, Size, Tooltip } from 'devextreme-react/chart'
import PivotGrid, {
Export,
FieldChooser,
@ -22,10 +17,9 @@ import PivotGrid, {
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 { 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'
@ -93,39 +87,45 @@ const Pivot = (props: PivotProps) => {
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()
@ -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(
<Notification type="danger" duration={2500}>
{translate('::App.Common.ExportError') ?? 'Dışa aktarma sırasında hata oluştu.'}
</Notification>,
{ 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(
<Notification type="danger" duration={2500}>
{translate('::App.Common.ExportError') ?? 'Dışa aktarma sırasında hata oluştu.'}
</Notification>,
{ 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) => {
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 ">
<div className="flex justify-end items-center">
<div className="relative pb-1 flex gap-1 border-b-1">
<Button
size="xs"
variant={'default'}
@ -437,21 +438,23 @@ const Pivot = (props: PivotProps) => {
const ds = instance.getDataSource()
if (ds && typeof ds.state === 'function') {
const currentState = ds.state()
customSaveState(currentState).then(() => {
toast.push(
<Notification type="success" duration={2000}>
{translate('::ListForms.ListForm.GridStateSaved')}
</Notification>,
{ placement: 'top-end' },
)
}).catch(() => {
toast.push(
<Notification type="danger" duration={2500}>
{translate('::ListForms.ListForm.GridStateSaveError')}
</Notification>,
{ placement: 'top-end' },
)
})
customSaveState(currentState)
.then(() => {
toast.push(
<Notification type="success" duration={2000}>
{translate('::ListForms.ListForm.GridStateSaved')}
</Notification>,
{ placement: 'top-end' },
)
})
.catch(() => {
toast.push(
<Notification type="danger" duration={2500}>
{translate('::ListForms.ListForm.GridStateSaveError')}
</Notification>,
{ placement: 'top-end' },
)
})
}
}
}}
@ -541,7 +544,9 @@ const Pivot = (props: PivotProps) => {
height={500}
/>
<LoadPanel
enabled={gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled as boolean | undefined}
enabled={
gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled as boolean | undefined
}
text={gridDto.gridOptions.pagerOptionDto?.loadPanelText}
/>
<Scrolling mode={gridDto.gridOptions.pagerOptionDto.scrollingMode} />

View file

@ -101,14 +101,6 @@ const Tree = (props: TreeProps) => {
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
const config = useStoreState((state) => state.abpConfig.config)
const preloadExportLibs = () => {
import('exceljs')
import('file-saver')
import('devextreme/excel_exporter')
import('jspdf')
import('devextreme/pdf_exporter')
}
type EditorOptionsWithButtons = {
buttons?: any[]
} & Record<string, any>

View file

@ -275,50 +275,50 @@ const useListFormCustomDataSource = ({
return null
}
},
// totalCount: async (loadOptions) => {
// const parameters = getLoadOptions(loadOptions, {
// listFormCode,
// filter: '',
// createDeleteQuery: searchParams?.get('createDeleteQuery'),
// group: '',
// })
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
// 1. Default filter'ı al
const defaultFilter = searchParams?.get('filter')
? JSON.parse(searchParams.get('filter')!)
: null
// let combinedFilter: any = parameters.filter
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
// }
// 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
// }
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
// }
// },
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 },