CardListView ve css düzenlemesi

This commit is contained in:
Sedat Öztürk 2025-11-30 18:15:02 +03:00
parent 0d69ae05ce
commit 737239b62a
74 changed files with 1095 additions and 913598 deletions

View file

@ -239,7 +239,15 @@
"dx.material.purple.light.compact": "App.Setting.material.purple.light.compact", "dx.material.purple.light.compact": "App.Setting.material.purple.light.compact",
"dx.material.purple.dark.compact": "App.Setting.material.purple.dark.compact", "dx.material.purple.dark.compact": "App.Setting.material.purple.dark.compact",
"dx.material.teal.light.compact": "App.Setting.material.teal.light.compact", "dx.material.teal.light.compact": "App.Setting.material.teal.light.compact",
"dx.material.teal.dark.compact": "App.Setting.material.teal.dark.compact" "dx.material.teal.dark.compact": "App.Setting.material.teal.dark.compact",
"dx.fluent.blue.dark.compact": "App.Setting.fluent.blue.dark.compact",
"dx.fluent.blue.dark": "App.Setting.fluent.blue.dark",
"dx.fluent.blue.light.compact": "App.Setting.fluent.blue.light.compact",
"dx.fluent.blue.light": "App.Setting.fluent.blue.light",
"dx.fluent.saas.dark.compact": "App.Setting.fluent.saas.dark.compact",
"dx.fluent.saas.dark": "App.Setting.fluent.saas.dark",
"dx.fluent.saas.light.compact": "App.Setting.fluent.saas.light.compact",
"dx.fluent.saas.light": "App.Setting.fluent.saas.light"
}, },
"order": 3 "order": 3
}, },

View file

@ -232,6 +232,14 @@ public static class PlatformConsts
{ "dx.material.purple.dark.compact",$"{Prefix.App}.Setting.material.purple.dark.compact" }, { "dx.material.purple.dark.compact",$"{Prefix.App}.Setting.material.purple.dark.compact" },
{ "dx.material.teal.light.compact", $"{Prefix.App}.Setting.material.teal.light.compact" }, { "dx.material.teal.light.compact", $"{Prefix.App}.Setting.material.teal.light.compact" },
{ "dx.material.teal.dark.compact", $"{Prefix.App}.Setting.material.teal.dark.compact" }, { "dx.material.teal.dark.compact", $"{Prefix.App}.Setting.material.teal.dark.compact" },
{ "dx.fluent.blue.dark.compact", $"{Prefix.App}.Setting.fluent.blue.dark.compact" },
{ "dx.fluent.blue.dark", $"{Prefix.App}.Setting.fluent.blue.dark" },
{ "dx.fluent.blue.light.compact", $"{Prefix.App}.Setting.fluent.blue.light.compact" },
{ "dx.fluent.blue.light", $"{Prefix.App}.Setting.fluent.blue.light" },
{ "dx.fluent.saas.dark.compact", $"{Prefix.App}.Setting.fluent.saas.dark.compact" },
{ "dx.fluent.saas.dark", $"{Prefix.App}.Setting.fluent.saas.dark" },
{ "dx.fluent.saas.light.compact", $"{Prefix.App}.Setting.fluent.saas.light.compact" },
{ "dx.fluent.saas.light", $"{Prefix.App}.Setting.fluent.saas.light" },
}; };
} }
} }

View file

@ -263,6 +263,14 @@ public static class SeedConsts
{ "dx.material.purple.dark.compact",$"{Prefix.App}.Setting.material.purple.dark.compact" }, { "dx.material.purple.dark.compact",$"{Prefix.App}.Setting.material.purple.dark.compact" },
{ "dx.material.teal.light.compact", $"{Prefix.App}.Setting.material.teal.light.compact" }, { "dx.material.teal.light.compact", $"{Prefix.App}.Setting.material.teal.light.compact" },
{ "dx.material.teal.dark.compact", $"{Prefix.App}.Setting.material.teal.dark.compact" }, { "dx.material.teal.dark.compact", $"{Prefix.App}.Setting.material.teal.dark.compact" },
{ "dx.fluent.blue.dark.compact", $"{Prefix.App}.Setting.fluent.blue.dark.compact" },
{ "dx.fluent.blue.dark", $"{Prefix.App}.Setting.fluent.blue.dark" },
{ "dx.fluent.blue.light.compact", $"{Prefix.App}.Setting.fluent.blue.light.compact" },
{ "dx.fluent.blue.light", $"{Prefix.App}.Setting.fluent.blue.light" },
{ "dx.fluent.saas.dark.compact", $"{Prefix.App}.Setting.fluent.saas.dark.compact" },
{ "dx.fluent.saas.dark", $"{Prefix.App}.Setting.fluent.saas.dark" },
{ "dx.fluent.saas.light.compact", $"{Prefix.App}.Setting.fluent.saas.light.compact" },
{ "dx.fluent.saas.light", $"{Prefix.App}.Setting.fluent.saas.light" },
}; };
} }
} }

10
ui/package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "erp-platform-ui", "name": "erp-platform-ui",
"version": "1.0.1", "version": "1.0.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "erp-platform-ui", "name": "erp-platform-ui",
"version": "1.0.1", "version": "1.0.2",
"dependencies": { "dependencies": {
"@babel/generator": "^7.28.3", "@babel/generator": "^7.28.3",
"@babel/parser": "^7.28.0", "@babel/parser": "^7.28.0",
@ -30,6 +30,7 @@
"classnames": "^2.5.1", "classnames": "^2.5.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"devextreme": "25.1.7", "devextreme": "25.1.7",
"devextreme-dist": "25.1.7",
"devextreme-react": "25.1.7", "devextreme-react": "25.1.7",
"easy-peasy": "^6.0.5", "easy-peasy": "^6.0.5",
"emoji-picker-react": "^4.14.1", "emoji-picker-react": "^4.14.1",
@ -5783,6 +5784,11 @@
"devextreme-bundler-init": "bin/bundler-init.js" "devextreme-bundler-init": "bin/bundler-init.js"
} }
}, },
"node_modules/devextreme-dist": {
"version": "25.1.7",
"resolved": "https://registry.npmjs.org/devextreme-dist/-/devextreme-dist-25.1.7.tgz",
"integrity": "sha512-pgTiadUS239aL8RJo9aO+H0DzCaufM/pp+oY6C1Oo36Wk2/zScUNmjAytJDfEeHOm/eChXi3EtLn1FHvHstlcQ=="
},
"node_modules/devextreme-quill": { "node_modules/devextreme-quill": {
"version": "1.7.6", "version": "1.7.6",
"resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.6.tgz", "resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.6.tgz",

View file

@ -1,7 +1,7 @@
{ {
"name": "erp-platform-ui", "name": "erp-platform-ui",
"private": true, "private": true,
"version": "1.0.1", "version": "1.0.2",
"elstarVersion": "2.1.6", "elstarVersion": "2.1.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -38,6 +38,7 @@
"classnames": "^2.5.1", "classnames": "^2.5.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"devextreme": "25.1.7", "devextreme": "25.1.7",
"devextreme-dist": "25.1.7",
"devextreme-react": "25.1.7", "devextreme-react": "25.1.7",
"easy-peasy": "^6.0.5", "easy-peasy": "^6.0.5",
"emoji-picker-react": "^4.14.1", "emoji-picker-react": "^4.14.1",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,9 +1,9 @@
/** /**
* DevExtreme (dx.common.css) * DevExtreme (dx.common.css)
* Version: 23.1.5 * Version: 25.1.7
* Build date: Wed Aug 30 2023 * Build date: Mon Nov 10 2025
* *
* Copyright (c) 2012 - 2023 Developer Express Inc. ALL RIGHTS RESERVED * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/ */
/*! /*!

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,828 @@
import Container from '@/components/shared/Container'
import { Dialog } from '@/components/ui'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import {
DbTypeEnum,
EditingFormItemDto,
GridDto,
PlatformEditorTypes,
} from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import useResponsive from '@/utils/hooks/useResponsive'
import { Template } from 'devextreme-react/core/template'
import CardView, {
CardViewRef,
Editing,
Pager,
Paging,
SearchPanel,
Selection,
LoadPanel,
Sorting,
} from 'devextreme-react/card-view'
import { captionize } from 'devextreme/core/utils/inflector'
import CustomStore from 'devextreme/data/custom_store'
import { useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet'
import SubForms from '../form/SubForms'
import { RowMode, SimpleItemWithColData } from '../form/types'
import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs'
import {
addCss,
addJs,
autoNumber,
GridExtraFilterState,
} from './Utils'
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { useFilters } from './useFilters'
import { useToolbar } from './useToolbar'
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
import { getList } from '@/services/form.service'
import { layoutTypes } from '../admin/listForm/edit/types'
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
import { useListFormColumns } from './useListFormColumns'
interface CardListViewProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const CardListView = (props: CardListViewProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
const { smaller } = useResponsive()
const cardViewRef = useRef<CardViewRef>()
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const [cardDataSource, setCardDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>()
const [formData, setFormData] = useState<any>()
const [mode, setMode] = useState<RowMode>('view')
const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([])
const [gridDto, setGridDto] = useState<GridDto>()
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const isInitializedRef = useRef(false)
const lastGridDtoRef = useRef<GridDto | null>(null)
type EditorOptionsWithButtons = {
buttons?: any[]
} & Record<string, any>
const defaultSearchParams = useRef<string | null>(null)
const searchParamsString = useRef<string>('')
useEffect(() => {
const initializeCardView = async () => {
const response = await getList({ listFormCode })
setGridDto(response.data)
}
if (extGridDto === undefined) {
initializeCardView()
} else {
setGridDto(extGridDto)
}
}, [listFormCode, extGridDto])
useEffect(() => {
if (!defaultSearchParams.current) {
defaultSearchParams.current = searchParams?.get('filter') ?? null
}
// searchParams'ı string olarak sakla ki dependency'lerde sorun çıkarmasın
searchParamsString.current = searchParams?.toString() || ''
}, [searchParams])
const layout = layoutTypes.card
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
getFilter,
layout,
})
const { filterToolbarData, ...filterData } = useFilters({
gridDto,
gridRef: cardViewRef as any,
listFormCode,
})
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: cardViewRef as any })
const { getBandedColumns } = useListFormColumns({
gridDto,
listFormCode,
isSubForm,
gridRef: cardViewRef as any,
})
function extractSearchParamsFields(filter: any): [string, string, any][] {
if (!Array.isArray(filter)) return []
// [field, op, val] formu mu?
if (filter.length === 3 && typeof filter[0] === 'string') {
return [filter as [string, string, any]]
}
// içinde başka filter array'leri olabilir
return filter.flatMap((f) => extractSearchParamsFields(f))
}
function getSelectedRowKeys() {
const card = cardViewRef.current?.instance()
if (!card) {
return []
}
return (card.option('selectedItemKeys') as any[]) || []
}
function getSelectedRowsData() {
const card = cardViewRef.current?.instance()
if (!card) {
return []
}
return (card.option('selectedItems') as any[]) || []
}
function refreshData() {
const instance = cardViewRef.current?.instance()
if (instance) {
instance.getDataSource()?.reload()
}
}
function getFilter() {
const card = cardViewRef.current?.instance()
if (!card) {
return
}
return card.option('searchExpr')
}
function onSelectionChanged(data: any) {
const grdOpt = gridDto?.gridOptions
const card = cardViewRef.current?.instance()
if (!grdOpt || !card) {
return
}
// kullanicinin yetkisi varsa ve birden fazla kayit secili ise coklu silme gorunsun
// CardView'de toolbar desteği sınırlı olduğu için bu özellik devre dışı
// SubForm'ları gösterebilmek için secili satiri formData'ya at
if (data.selectedItems.length) {
setFormData(data.selectedItems[0])
}
}
function onEditorPreparing(editor: any) {
if (editor.dataField && gridDto) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.find((i) => i.dataField === editor.dataField)
// Cascade disabled mantığı
const colFormat = gridDto.columnFormats.find((c) => c.fieldName === editor.dataField)
if (colFormat?.lookupDto?.cascadeParentFields) {
const parentFields = colFormat.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const prevHandler = editor.editorOptions.onValueChanged
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
// Parent field değiştiğinde tüm cascade childları kontrol et
const card = editor.component
const dataSource = card.getDataSource()
const itemData = dataSource?.items?.() || []
// Bu field bir parent ise, child fieldleri temizle
if (colFormat.lookupDto?.cascadeEmptyFields) {
const childFields = colFormat.lookupDto.cascadeEmptyFields
.split(',')
.map((f: string) => f.trim())
// CardView'de form üzerinden güncelleme yapılmalı
const formInstance = card.option('editing.form') as any
if (formInstance && formInstance.updateData) {
const updates: any = {}
childFields.forEach((childField: string) => {
updates[childField] = null
})
formInstance.updateData(updates)
}
}
// Tüm cascade fieldlerin disabled durumlarını güncelle
setTimeout(() => {
const popup = card.option('editing.popup')
if (popup) {
const formInstance = card.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !itemData[pf])
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
editorInstance.option('disabled', shouldDisable)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
}
}
})
}
}
}, 50)
}
// İlk açılışta disabled durumunu kontrol et
const card = editor.component
const formInstance = card.option('editing.form') as any
const formData = formInstance?.option?.('formData') || {}
const shouldDisable = parentFields.some((pf: string) => !formData[pf])
if (shouldDisable) {
editor.editorOptions.disabled = true
}
}
if (formItem?.editorScript) {
const prevHandler = editor.editorOptions.onValueChanged
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
try {
const card = editor.component
const formInstance = card.option('editing.form') as any
const formData = formInstance?.option?.('formData') || {}
const setFormData = (newData: any) => {
if (formInstance && formInstance.updateData) {
formInstance.updateData(newData)
}
}
eval(formItem.editorScript!)
} catch (err) {
console.error('Script exec error', formItem.dataField, err)
}
}
}
if (editor.editorOptions?.buttons) {
editor.editorOptions.buttons = editor.editorOptions.buttons.map((btn: any) => {
if (btn?.options?.onClick && typeof btn.options.onClick === 'function') {
const origClick = btn.options.onClick
btn.options.onClick = (e: any) => {
const card = editor.component
const formInstance = card.option('editing.form') as any
const formData = formInstance?.option?.('formData') || {}
origClick({
...e,
formData,
fieldName: editor.dataField,
})
}
}
return btn
})
}
}
}
useEffect(() => {
if (refListFormCode.current !== listFormCode) {
// listFormCode değiştiğinde yeniden initialize et
lastGridDtoRef.current = null
setExtraFilters([])
setCardDataSource(undefined)
setColumnData(undefined)
if (cardViewRef?.current) {
const instance = cardViewRef?.current?.instance()
if (instance) {
instance.option('dataSource', undefined)
}
}
}
}, [listFormCode])
useEffect(() => {
// gridDto gerçekten değişmişse işlem yap
if (!gridDto || lastGridDtoRef.current === gridDto) {
return
}
console.log('CardListView: useEffect triggered for gridDto', listFormCode)
lastGridDtoRef.current = gridDto
// 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)
}
}
// Kolonları hazırla
const cols = getBandedColumns()
cols?.forEach((col) => {
const eo = col.editorOptions
// Sadece phoneGlobal formatlı kolonlarda çalış
if (eo?.format === 'phoneGlobal') {
col.dataType = 'string'
const formatter = (value: any) => {
if (!value) return ''
let digits = String(value).replace(/\D/g, '')
if (digits.startsWith('90') && digits.length > 10) digits = digits.slice(-10)
if (digits.startsWith('0') && digits.length > 10) digits = digits.slice(-10)
if (digits.length > 10) digits = digits.slice(-10)
if (digits.length !== 10) return ''
const match = digits.match(/^(\d{3})(\d{3})(\d{4})$/)
return match ? `(${match[1]}) ${match[2]}-${match[3]}` : digits
}
col.format = { formatter }
col.customizeText = (cellInfo: any) => formatter(cellInfo?.value)
}
})
// DataSource'u sadece bir kez oluştur
const dataSource = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layoutTypes.card,
cols,
)
console.log('CardListView: Setting new dataSource')
// Tüm state'leri sırayla güncelle
setColumnData(cols)
setCardDataSource(dataSource)
if (gridDto?.gridOptions.extraFilterDto) {
setExtraFilters(
gridDto.gridOptions.extraFilterDto.map((f) => ({
fieldName: f.fieldName,
operator: f.operator,
controlType: f.controlType,
value: f.defaultValue ?? '',
})),
)
}
if (gridDto.gridOptions.editingOptionDto?.popup) {
setIsPopupFullScreen(gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [gridDto])
// extraFilters useEffect kaldırıldı - filtreler dataSource seviyesinde işlenir
useEffect(() => {
refListFormCode.current = listFormCode
}, [listFormCode])
// WidgetGroup yüksekliğini hesapla
useEffect(() => {
const calculateWidgetHeight = () => {
if (widgetGroupRef.current) {
const height = widgetGroupRef.current.offsetHeight
setWidgetGroupHeight(height)
}
}
// İlk render'da hesapla
calculateWidgetHeight()
// Resize durumunda tekrar hesapla
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
if (widgetGroupRef.current) {
resizeObserver.observe(widgetGroupRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [gridDto?.widgets])
return (
<>
<div ref={widgetGroupRef}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}>
{!isSubForm && (
<Helmet
titleTemplate="%s | Erp Platform"
title={translate('::' + gridDto?.gridOptions.title)}
defaultTitle="Erp Platform"
></Helmet>
)}
{!gridDto && <div className="p-4">Loading card configuration...</div>}
{gridDto && !columnData && <div className="p-4">Loading columns...</div>}
{gridDto && columnData && !cardDataSource && (
<div className="p-4">Loading data source...</div>
)}
{gridDto &&
columnData &&
cardDataSource &&
(() => {
return true
})() && (
<>
<div className="p-1">
<CardView
ref={cardViewRef as any}
key={`CardView-${listFormCode}`}
id={'CardView-' + listFormCode}
dataSource={cardDataSource}
height={
gridDto.gridOptions.height > 0
? gridDto.gridOptions.height
: gridDto.gridOptions.fullHeight
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
width={gridDto.gridOptions.width || '100%'}
onSelectionChanged={onSelectionChanged}
focusStateEnabled={true}
hoverStateEnabled={true}
elementAttr={{
class: 'custom-card-view'
}}
>
<Editing
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
popup={{
title:
(mode === 'new' ? '✚ ' : '🖊️ ') +
translate('::' + gridDto.gridOptions.editingOptionDto?.popup?.title),
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
hideOnOutsideClick:
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
fullScreen: isPopupFullScreen,
toolbarItems: [
{
widget: 'dxButton',
toolbar: 'bottom',
location: 'after',
options: {
text: translate('::Save'),
type: 'default',
onClick: () => {
const card = cardViewRef.current?.instance()
card?.saveEditData()
},
},
},
{
widget: 'dxButton',
toolbar: 'bottom',
location: 'after',
options: {
text: translate('::Cancel'),
onClick: () => {
const card = cardViewRef.current?.instance()
card?.cancelEditData()
},
},
},
{
widget: 'dxButton',
toolbar: 'top',
location: 'after',
options: {
icon: isPopupFullScreen ? 'collapse' : 'fullscreen',
hint: isPopupFullScreen
? translate('::Normal Boyut')
: translate('::Tam Ekran'),
stylingMode: 'text',
onClick: () => setIsPopupFullScreen(!isPopupFullScreen),
},
},
],
}}
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,
}
}
// Set defaultValue for @AUTONUMBER fields
if (
typeof listFormField?.defaultValue === 'string' &&
listFormField?.defaultValue === '@AUTONUMBER' &&
mode === 'new'
) {
editorOptions = {
...editorOptions,
value: autoNumber(),
}
}
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') {
const effectiveColCount = e.colCount || 1
const effectiveColSpan = e.colSpan || 1
result.push({
itemType: e.itemType,
colCount: effectiveColCount,
colSpan: effectiveColSpan,
caption: e.caption,
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]) {
result.push({
itemType: 'tabbed',
colCount: 1,
colSpan: 1,
tabs: tabbedItems.map((tabbedItem: any) => {
const effectiveColCount = tabbedItem.colCount || 1
return {
title: tabbedItem.caption,
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={'cellEditGridBox'} render={GridBoxEditorComponent} />
<Template name="extraFilters">
<GridExtraFilterToolbar
filters={gridDto?.gridOptions.extraFilterDto ?? []}
extraFilters={extraFilters}
setExtraFilters={setExtraFilters}
/>
</Template>
<Sorting mode={gridDto.gridOptions?.sortMode}></Sorting>
<SearchPanel
visible={gridDto.gridOptions.searchPanelDto.visible}
width={gridDto.gridOptions.searchPanelDto.width}
></SearchPanel>
<Selection
mode={gridDto.gridOptions.selectionDto?.mode}
selectAllMode={gridDto.gridOptions.selectionDto?.selectAllMode}
></Selection>
<Paging defaultPageSize={gridDto.gridOptions.pageSize ?? 20} />
<Pager
visible={gridDto.gridOptions.pagerOptionDto?.visible}
allowedPageSizes={gridDto.gridOptions.pagerOptionDto?.allowedPageSizes
?.split(',')
.map((a: any) => +a)}
showPageSizeSelector={gridDto.gridOptions.pagerOptionDto?.showPageSizeSelector}
showInfo={false}
showNavigationButtons={
gridDto.gridOptions.pagerOptionDto?.showNavigationButtons
}
displayMode={gridDto.gridOptions.pagerOptionDto?.displayMode}
></Pager>
<LoadPanel
visible={gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled === true || gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled === 'auto'}
></LoadPanel>
</CardView>
{gridDto?.gridOptions?.subFormsDto?.length > 0 && (
<>
<hr className="my-2" />
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} />
</>
)}
<Dialog
width={smaller.md ? '100%' : 1000}
isOpen={filterData.isImportModalOpen || false}
onClose={() => filterData.setIsImportModalOpen(false)}
onRequestClose={() => filterData.setIsImportModalOpen(false)}
>
<ImportDashboard gridDto={gridDto} />
</Dialog>
</div>
</>
)}
<Dialog
isOpen={toolbarModalData?.open || false}
onClose={() => setToolbarModalData(undefined)}
onRequestClose={() => setToolbarModalData(undefined)}
>
{toolbarModalData?.content}
</Dialog>
<GridFilterDialogs gridRef={cardViewRef as any} listFormCode={listFormCode} {...filterData} />
</Container>
</>
)
}
export default CardListView

View file

@ -7,7 +7,6 @@ import { useStoreActions, useStoreState } from '@/store/store'
import classNames from 'classnames' import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { GridDto } from '@/proxy/form/models' import { GridDto } from '@/proxy/form/models'
import Card from './Card'
import { Badge, Button } from '@/components/ui' import { Badge, Button } from '@/components/ui'
import Pivot from './Pivot' import Pivot from './Pivot'
import Tree from './Tree' import Tree from './Tree'
@ -15,6 +14,7 @@ import { getList } from '@/services/form.service'
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
import { ListViewLayoutType } from '../admin/listForm/edit/types' import { ListViewLayoutType } from '../admin/listForm/edit/types'
import Chart from './Chart' import Chart from './Chart'
import CardListView from './CardListView'
const List = () => { const List = () => {
const params = useParams() const params = useParams()
@ -182,7 +182,7 @@ const List = () => {
gridDto={gridDto} gridDto={gridDto}
/> />
) : viewMode === 'card' ? ( ) : viewMode === 'card' ? (
<Card <CardListView
listFormCode={listFormCode} listFormCode={listFormCode}
searchParams={searchParams} searchParams={searchParams}
isSubForm={false} isSubForm={false}

View file

@ -9,6 +9,7 @@ import { GridColumnData } from './GridColumnData'
import { dynamicFetch } from '@/services/form.service' import { dynamicFetch } from '@/services/form.service'
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant' import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
import { TreeListRef } from 'devextreme-react/cjs/tree-list' import { TreeListRef } from 'devextreme-react/cjs/tree-list'
import { CardViewRef } from 'devextreme-react/cjs/card-view'
const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk
@ -19,6 +20,7 @@ const useListFormCustomDataSource = ({
| MutableRefObject<DataGridRef<any, any> | undefined> | MutableRefObject<DataGridRef<any, any> | undefined>
| MutableRefObject<PivotGridRef | undefined> | MutableRefObject<PivotGridRef | undefined>
| MutableRefObject<TreeListRef<any, any> | undefined> | MutableRefObject<TreeListRef<any, any> | undefined>
| MutableRefObject<CardViewRef<any, any> | undefined>
}) => { }) => {
const createSelectDataSource = useCallback( const createSelectDataSource = useCallback(
( (