Tree ve Grid Popup form düzenlemesi
This commit is contained in:
parent
50fe2907d1
commit
8d97c06dd0
4 changed files with 228 additions and 21 deletions
|
|
@ -2702,7 +2702,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
}),
|
}),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson,
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson,
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new FieldsDefaultValue() { FieldName = "IsDisabled", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value }
|
new() { FieldName = "IsDisabled", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -979,16 +979,14 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
sortedFormDto.forEach((e: any) => {
|
sortedFormDto.forEach((e: any) => {
|
||||||
if (e.itemType !== 'tabbed') {
|
if (e.itemType !== 'tabbed') {
|
||||||
// colCount: max(endpoint.colCount, max(item.colSpan))
|
// Backend'den gelen colCount ve colSpan değerlerini kullan
|
||||||
const maxItemColSpan = Math.max(
|
const effectiveColCount = e.colCount || 1
|
||||||
...(e.items?.map((i: any) => i.colSpan || 1) || [1]),
|
const effectiveColSpan = e.colSpan || 1
|
||||||
)
|
|
||||||
const effectiveColCount = Math.max(maxItemColSpan, e.colCount || 1)
|
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
itemType: e.itemType,
|
itemType: e.itemType,
|
||||||
colCount: effectiveColCount,
|
colCount: effectiveColCount,
|
||||||
colSpan: e.colSpan,
|
colSpan: effectiveColSpan,
|
||||||
caption: e.caption, // Group'larda caption olmalı
|
caption: e.caption, // Group'larda caption olmalı
|
||||||
items: e.items
|
items: e.items
|
||||||
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
||||||
|
|
@ -1013,14 +1011,8 @@ const Grid = (props: GridProps) => {
|
||||||
colSpan: 1,
|
colSpan: 1,
|
||||||
// caption kullanma! Tabs içindeki title'lar yeterli
|
// caption kullanma! Tabs içindeki title'lar yeterli
|
||||||
tabs: tabbedItems.map((tabbedItem: any) => {
|
tabs: tabbedItems.map((tabbedItem: any) => {
|
||||||
// Tab için colCount: max(endpoint.colCount, max(item.colSpan))
|
// Backend'den gelen colCount ve colSpan değerlerini kullan
|
||||||
const maxItemColSpan = Math.max(
|
const effectiveColCount = tabbedItem.colCount || 1
|
||||||
...(tabbedItem.items?.map((i: any) => i.colSpan || 1) || [1]),
|
|
||||||
)
|
|
||||||
const effectiveColCount = Math.max(
|
|
||||||
maxItemColSpan,
|
|
||||||
tabbedItem.colCount || 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: tabbedItem.caption, // Her tab'ın title'ı
|
title: tabbedItem.caption, // Her tab'ın title'ı
|
||||||
|
|
@ -1094,7 +1086,6 @@ const Grid = (props: GridProps) => {
|
||||||
></Selection>
|
></Selection>
|
||||||
<Paging
|
<Paging
|
||||||
defaultPageSize={gridDto.gridOptions.pageSize ?? 20}
|
defaultPageSize={gridDto.gridOptions.pageSize ?? 20}
|
||||||
pageSize={gridDto.gridOptions.pageSize ?? 20}
|
|
||||||
/>
|
/>
|
||||||
<Pager
|
<Pager
|
||||||
visible={gridDto.gridOptions.pagerOptionDto?.visible}
|
visible={gridDto.gridOptions.pagerOptionDto?.visible}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import Container from '@/components/shared/Container'
|
import Container from '@/components/shared/Container'
|
||||||
import { Dialog, Notification, toast } from '@/components/ui'
|
import { Dialog, Notification, toast } from '@/components/ui'
|
||||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||||
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
import { DbTypeEnum, EditingFormItemDto, GridDto, ListFormCustomizationTypeEnum, PlatformEditorTypes } from '@/proxy/form/models'
|
||||||
import {
|
import {
|
||||||
getListFormCustomization,
|
getListFormCustomization,
|
||||||
postListFormCustomization,
|
postListFormCustomization,
|
||||||
|
|
@ -11,6 +11,7 @@ import { useListFormColumns } from '@/shared/useListFormColumns'
|
||||||
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
|
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import useResponsive from '@/utils/hooks/useResponsive'
|
import useResponsive from '@/utils/hooks/useResponsive'
|
||||||
|
import { captionize } from 'devextreme/core/utils/inflector'
|
||||||
import { Template } from 'devextreme-react/core/template'
|
import { Template } from 'devextreme-react/core/template'
|
||||||
import TreeListDx, {
|
import TreeListDx, {
|
||||||
ColumnChooser,
|
ColumnChooser,
|
||||||
|
|
@ -35,7 +36,7 @@ import { Item } from 'devextreme-react/toolbar'
|
||||||
import CustomStore from 'devextreme/data/custom_store'
|
import CustomStore from 'devextreme/data/custom_store'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { RowMode } from '../form/types'
|
import { RowMode, SimpleItemWithColData } from '../form/types'
|
||||||
import { GridColumnData } from './GridColumnData'
|
import { GridColumnData } from './GridColumnData'
|
||||||
import GridFilterDialogs from './GridFilterDialogs'
|
import GridFilterDialogs from './GridFilterDialogs'
|
||||||
import {
|
import {
|
||||||
|
|
@ -82,6 +83,18 @@ const Tree = (props: TreeProps) => {
|
||||||
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
||||||
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
|
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
|
||||||
|
|
||||||
|
const preloadExportLibs = () => {
|
||||||
|
import('exceljs')
|
||||||
|
import('file-saver')
|
||||||
|
import('devextreme/excel_exporter')
|
||||||
|
import('jspdf')
|
||||||
|
import('devextreme/pdf_exporter')
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditorOptionsWithButtons = {
|
||||||
|
buttons?: any[]
|
||||||
|
} & Record<string, any>
|
||||||
|
|
||||||
const defaultSearchParams = useRef<string | null>(null)
|
const defaultSearchParams = useRef<string | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -112,6 +125,7 @@ const Tree = (props: TreeProps) => {
|
||||||
expandAll,
|
expandAll,
|
||||||
collapseAll,
|
collapseAll,
|
||||||
getFilter,
|
getFilter,
|
||||||
|
isTree: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const { filterToolbarData, ...filterData } = useFilters({
|
const { filterToolbarData, ...filterData } = useFilters({
|
||||||
|
|
@ -630,6 +644,9 @@ const Tree = (props: TreeProps) => {
|
||||||
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
||||||
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
||||||
useIcons={gridDto.gridOptions.editingOptionDto?.useIcons}
|
useIcons={gridDto.gridOptions.editingOptionDto?.useIcons}
|
||||||
|
confirmDelete={gridDto.gridOptions.editingOptionDto?.confirmDelete}
|
||||||
|
selectTextOnEditStart={gridDto.gridOptions.editingOptionDto?.selectTextOnEditStart}
|
||||||
|
startEditAction={gridDto.gridOptions.editingOptionDto?.startEditAction}
|
||||||
popup={{
|
popup={{
|
||||||
title:
|
title:
|
||||||
(mode === 'new' ? '✚ ' : '🖊️ ') +
|
(mode === 'new' ? '✚ ' : '🖊️ ') +
|
||||||
|
|
@ -657,7 +674,205 @@ const Tree = (props: TreeProps) => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
/>
|
form={{
|
||||||
|
colCount: 1,
|
||||||
|
onFieldDataChanged: (e) => {
|
||||||
|
if (e.dataField) {
|
||||||
|
const formItem = gridDto.gridOptions.editingFormDto
|
||||||
|
.flatMap((group) => group.items || [])
|
||||||
|
.find((i) => i.dataField === e.dataField)
|
||||||
|
if (formItem?.editorScript) {
|
||||||
|
try {
|
||||||
|
eval(formItem.editorScript)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Script exec error', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items:
|
||||||
|
gridDto.gridOptions.editingFormDto?.length > 0
|
||||||
|
? (() => {
|
||||||
|
const sortedFormDto = gridDto.gridOptions.editingFormDto
|
||||||
|
.slice()
|
||||||
|
.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
||||||
|
|
||||||
|
// Tabbed item'ları grupla
|
||||||
|
const tabbedItems = sortedFormDto.filter(
|
||||||
|
(e: any) => e.itemType === 'tabbed',
|
||||||
|
)
|
||||||
|
const result: any[] = []
|
||||||
|
|
||||||
|
// Helper function: item mapper
|
||||||
|
const mapFormItem = (i: EditingFormItemDto) => {
|
||||||
|
let editorOptions: EditorOptionsWithButtons = {}
|
||||||
|
try {
|
||||||
|
editorOptions = i.editorOptions && JSON.parse(i.editorOptions)
|
||||||
|
|
||||||
|
if (editorOptions?.buttons) {
|
||||||
|
editorOptions.buttons = (editorOptions?.buttons || []).map(
|
||||||
|
(btn: any) => {
|
||||||
|
if (
|
||||||
|
btn?.options?.onClick &&
|
||||||
|
typeof btn.options.onClick === 'string'
|
||||||
|
) {
|
||||||
|
btn.options.onClick = eval(`(${btn.options.onClick})`)
|
||||||
|
}
|
||||||
|
return btn
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawFilter = searchParams?.get('filter')
|
||||||
|
if (rawFilter) {
|
||||||
|
const parsed = JSON.parse(rawFilter)
|
||||||
|
const filters = extractSearchParamsFields(parsed)
|
||||||
|
|
||||||
|
const hasFilter = filters.some(
|
||||||
|
([field, op, val]) => field === i.dataField,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (hasFilter) {
|
||||||
|
const existsInExtra = extraFilters.some(
|
||||||
|
(f) => f.fieldName === i.dataField && !!f.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!existsInExtra) {
|
||||||
|
editorOptions = {
|
||||||
|
...editorOptions,
|
||||||
|
readOnly: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const fieldName = i.dataField.split(':')[0]
|
||||||
|
const listFormField = gridDto.columnFormats.find(
|
||||||
|
(x: any) => x.fieldName === fieldName,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||||
|
editorOptions = {
|
||||||
|
...{
|
||||||
|
type: 'date',
|
||||||
|
dateSerializationFormat: 'yyyy-MM-dd',
|
||||||
|
displayFormat: 'shortDate',
|
||||||
|
},
|
||||||
|
...editorOptions,
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
||||||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
||||||
|
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
||||||
|
) {
|
||||||
|
editorOptions = {
|
||||||
|
...{
|
||||||
|
type: 'datetime',
|
||||||
|
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ssxxx',
|
||||||
|
displayFormat: 'shortDateShortTime',
|
||||||
|
},
|
||||||
|
...editorOptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const item: SimpleItemWithColData = {
|
||||||
|
canRead: listFormField?.canRead ?? false,
|
||||||
|
canUpdate: listFormField?.canUpdate ?? false,
|
||||||
|
canCreate: listFormField?.canCreate ?? false,
|
||||||
|
canExport: listFormField?.canExport ?? false,
|
||||||
|
dataField: i.dataField,
|
||||||
|
name: i.dataField,
|
||||||
|
editorType2: i.editorType2,
|
||||||
|
editorType:
|
||||||
|
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||||||
|
? 'dxDropDownBox'
|
||||||
|
: i.editorType2,
|
||||||
|
colSpan: i.colSpan,
|
||||||
|
isRequired: i.isRequired,
|
||||||
|
editorOptions,
|
||||||
|
editorScript: i.editorScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i.dataField.indexOf(':') >= 0) {
|
||||||
|
item.label = { text: captionize(i.dataField.split(':')[1]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(mode == 'edit' && !item.canUpdate) ||
|
||||||
|
(mode == 'new' && !item.canCreate)
|
||||||
|
) {
|
||||||
|
item.editorOptions = {
|
||||||
|
...item.editorOptions,
|
||||||
|
readOnly: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedFormDto.forEach((e: any) => {
|
||||||
|
if (e.itemType !== 'tabbed') {
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
itemType: e.itemType,
|
||||||
|
colCount: e.colCount,
|
||||||
|
colSpan: e.colSpan,
|
||||||
|
caption: e.caption, // Group'larda caption olmalı
|
||||||
|
items: e.items
|
||||||
|
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
||||||
|
.map(mapFormItem)
|
||||||
|
.filter((a: any) => {
|
||||||
|
if (mode === 'view') {
|
||||||
|
return a.canRead
|
||||||
|
} else if (mode === 'new') {
|
||||||
|
return a.canCreate || a.canRead
|
||||||
|
} else if (mode === 'edit') {
|
||||||
|
return a.canUpdate || a.canRead
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} else if (tabbedItems.length > 0 && e === tabbedItems[0]) {
|
||||||
|
// Tabbed için caption OLMAMALI - sadece tabs array içinde title kullan
|
||||||
|
result.push({
|
||||||
|
itemType: 'tabbed',
|
||||||
|
colCount: 1,
|
||||||
|
colSpan: 1,
|
||||||
|
// caption kullanma! Tabs içindeki title'lar yeterli
|
||||||
|
tabs: tabbedItems.map((tabbedItem: any) => {
|
||||||
|
// Backend'den gelen colCount ve colSpan değerlerini kullan
|
||||||
|
const effectiveColCount = tabbedItem.colCount || 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: tabbedItem.caption, // Her tab'ın title'ı
|
||||||
|
colCount: effectiveColCount,
|
||||||
|
items: tabbedItem.items
|
||||||
|
?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
||||||
|
.map(mapFormItem)
|
||||||
|
.filter((a: any) => {
|
||||||
|
if (mode === 'view') {
|
||||||
|
return a.canRead
|
||||||
|
} else if (mode === 'new') {
|
||||||
|
return a.canCreate || a.canRead
|
||||||
|
} else if (mode === 'edit') {
|
||||||
|
return a.canUpdate || a.canRead
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
})()
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
></Editing>
|
||||||
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
||||||
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
||||||
<Template name="extraFilters">
|
<Template name="extraFilters">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { ToolbarItem } from 'devextreme/ui/data_grid_types'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useDialogContext } from '../shared/DialogContext'
|
import { useDialogContext } from '../shared/DialogContext'
|
||||||
import { usePWA } from '@/utils/hooks/usePWA'
|
import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
import { text } from 'stream/consumers'
|
|
||||||
|
|
||||||
type ToolbarModalData = {
|
type ToolbarModalData = {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
|
@ -26,6 +25,7 @@ const useToolbar = ({
|
||||||
getFilter,
|
getFilter,
|
||||||
expandAll,
|
expandAll,
|
||||||
collapseAll,
|
collapseAll,
|
||||||
|
isTree = false,
|
||||||
}: {
|
}: {
|
||||||
gridDto?: GridDto
|
gridDto?: GridDto
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -35,6 +35,7 @@ const useToolbar = ({
|
||||||
getFilter: () => void
|
getFilter: () => void
|
||||||
expandAll?: () => void
|
expandAll?: () => void
|
||||||
collapseAll?: () => void
|
collapseAll?: () => void
|
||||||
|
isTree?: boolean
|
||||||
}): {
|
}): {
|
||||||
toolbarData: ToolbarItem[]
|
toolbarData: ToolbarItem[]
|
||||||
toolbarModalData: ToolbarModalData | undefined
|
toolbarModalData: ToolbarModalData | undefined
|
||||||
|
|
@ -79,7 +80,7 @@ const useToolbar = ({
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add Expand All button for TreeList
|
// Add Expand All button for TreeList
|
||||||
if (gridDto?.gridOptions.layoutDto.defaultLayout === 'tree') {
|
if (isTree) {
|
||||||
items.push({
|
items.push({
|
||||||
widget: 'dxButton',
|
widget: 'dxButton',
|
||||||
name: 'expandAllButton',
|
name: 'expandAllButton',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue