+ {!isSubForm && (
+
+ )}
+
+ {!gridDto && (
+
+ Loading CardView configuration...
+
+ )}
+
+ {gridDto && !cardViewDataSource && (
+
+ Loading data source...
+
+ )}
+
+ {gridDto && columnsWithLookup && Array.isArray(columnsWithLookup) && columnsWithLookup.length > 0 && cardViewDataSource && (
+
+
0
+ ? gridDto.gridOptions.height
+ : gridDto.gridOptions.fullHeight
+ ? `calc(100vh - ${170 + widgetGroupHeight}px)`
+ : undefined
+ }
+ remoteOperations={false}
+ onSelectionChanged={onSelectionChanged as any}
+ onInitNewCard={onInitNewCard as any}
+ onCardInserting={onCardInserting as any}
+ onCardUpdating={onCardUpdating as any}
+ onEditingStart={onEditingStart as any}
+ onDataErrorOccurred={onDataErrorOccurred as any}
+ onEditCanceled={() => {
+ setMode('view')
+ setIsPopupFullScreen(false)
+ }}
+ onCardInserted={() => {
+ setMode('view')
+ setIsPopupFullScreen(false)
+ // Küçük bir gecikme ile reload - server transaction commit bekle
+ setTimeout(() => {
+ refreshData()
+ props.refreshData?.()
+ }, 100)
+ }}
+ onCardUpdated={() => {
+ setMode('view')
+ setIsPopupFullScreen(false)
+ // Küçük bir gecikme ile reload - server transaction commit bekle
+ setTimeout(() => {
+ refreshData()
+ props.refreshData?.()
+ }, 100)
+ }}
+ onCardRemoved={() => {
+ // Küçük bir gecikme ile reload - server transaction commit bekle
+ setTimeout(() => {
+ refreshData()
+ props.refreshData?.()
+ }, 100)
+ }}
+ onOptionChanged={(e: any) => {
+ if (e.name === 'paging.pageSize' && e.value !== pageSize) {
+ setPageSize(e.value)
+ }
+ }}
+ onContentReady={() => {
+ // Lookup DataSource'ları yükle ve state'e cache'le (sadece ilk yüklemede)
+ if (lookupItemsCache.size === 0 && lookupDataSourcesRef.current.size > 0) {
+ const lookupPromises: Array> = []
+
+ lookupDataSourcesRef.current.forEach((ds, key) => {
+ if (ds && typeof ds.load === 'function') {
+ lookupPromises.push(
+ ds.load().then(() => ({
+ key,
+ items: ds.items() || [],
+ })),
+ )
+ }
+ })
+
+ if (lookupPromises.length > 0) {
+ Promise.all(lookupPromises)
+ .then((results) => {
+ const newCache = new Map()
+ results.forEach(({ key, items }) => {
+ newCache.set(key, items)
+ })
+ setLookupItemsCache(newCache)
+
+ const instance = cardViewRef.current?.instance()
+ if (instance) {
+ instance.repaint()
+ }
+ })
+ .catch(() => {
+ // Hata durumunda sessizce devam et
+ })
+ }
+ }
+ }}
+ >
+ {/* Toolbar */}
+
+
+ {/* Selection */}
+
+
+ {/* Sorting */}
+
+
+ {/* Header Filter */}
+
+
+ {/* Filter Panel */}
+
+
+ {/* Search Panel */}
+
+
+ {/* Column Chooser */}
+
+
+ {/* Paging */}
+
+
+ {/* Pager */}
+
+
+ {/* Editing */}
+ {
+ const cardView = cardViewRef.current?.instance()
+ if (cardView) {
+ // Form validasyonu yap
+ const editForm = (cardView as any).getController?.('validating')?.validate?.()
+
+ // Eğer validate fonksiyonu yoksa direkt kaydet
+ if (!editForm) {
+ cardView.saveEditData()
+ return
+ }
+
+ // Validasyon hatası varsa kaydetme
+ if (editForm && !editForm.brokenRules?.length) {
+ cardView.saveEditData()
+ }
+ }
+ },
+ },
+ },
+ {
+ widget: 'dxButton',
+ toolbar: 'bottom',
+ location: 'after',
+ options: {
+ text: translate('::Cancel'),
+ onClick: () => {
+ const cardView = cardViewRef.current?.instance()
+ cardView?.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: gridDto.gridOptions.editingFormDto?.[0]?.colCount || 2,
+ showValidationSummary: true,
+ items:
+ gridDto.gridOptions.editingFormDto?.length > 0
+ ? (() => {
+ const sortedFormDto = gridDto.gridOptions.editingFormDto
+ .slice()
+ .sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
+
+ const tabbedItems = sortedFormDto.filter(
+ (e: any) => e.itemType === 'tabbed',
+ )
+ const result: any[] = []
+
+ const mapFormItem = (i: EditingFormItemDto) => {
+ let editorOptions: any = {}
+ try {
+ if (i.editorOptions) {
+ editorOptions = JSON.parse(i.editorOptions)
+ }
+ } 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:ss',
+ displayFormat: 'shortDateShortTime',
+ },
+ ...editorOptions,
+ }
+ }
+
+ // Set defaultValue for @AUTONUMBER fields
+ if (
+ typeof listFormField?.defaultValue === 'string' &&
+ listFormField?.defaultValue === '@AUTONUMBER' &&
+ mode === 'new'
+ ) {
+ editorOptions = {
+ ...editorOptions,
+ value: autoNumber(),
+ }
+ }
+
+ // EditorType belirleme
+ let editorType: any = i.editorType2 || i.editorType
+ if (i.editorType2 === PlatformEditorTypes.dxGridBox) {
+ editorType = 'dxDropDownBox'
+ } else if (i.editorType2) {
+ editorType = i.editorType2
+ }
+
+ // Lookup DataSource oluştur
+ if (
+ listFormField?.lookupDto &&
+ listFormField.lookupDto.dataSourceType > 0
+ ) {
+ const cacheKey = `${listFormField.fieldName}`
+ let lookupDs = lookupDataSourcesRef.current.get(cacheKey)
+
+ if (!lookupDs) {
+ lookupDs = lookupDataSource(null, listFormField)
+ lookupDataSourcesRef.current.set(cacheKey, lookupDs)
+ }
+
+ editorOptions.dataSource = lookupDs
+ editorOptions.valueExpr =
+ listFormField.lookupDto.valueExpr?.toLowerCase() || 'key'
+ editorOptions.displayExpr =
+ listFormField.lookupDto.displayExpr?.toLowerCase() || 'name'
+ editorOptions.searchEnabled = true
+ editorOptions.showClearButton = true
+
+ if (!editorType || editorType === 'dxTextBox') {
+ editorType = 'dxSelectBox'
+ }
+ }
+
+ const item: any = {
+ dataField: i.dataField,
+ editorType: editorType,
+ colSpan: i.colSpan,
+ isRequired: i.isRequired,
+ editorOptions,
+ }
+
+ // Required field için validasyon kuralı ekle
+ if (i.isRequired) {
+ item.validationRules = [
+ {
+ type: 'required',
+ message: `${i.dataField.split(':')[1] || i.dataField} zorunludur`,
+ },
+ ]
+ }
+
+ if (i.dataField.indexOf(':') >= 0) {
+ item.label = { text: captionize(i.dataField.split(':')[1]) }
+ }
+
+ return item
+ }
+
+ sortedFormDto.forEach((e: any) => {
+ if (e.itemType !== 'tabbed') {
+ result.push({
+ itemType: e.itemType,
+ colCount: e.colCount || 1,
+ colSpan: e.colSpan || 1,
+ caption: e.caption,
+ items: e.items
+ ?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
+ .map(mapFormItem),
+ })
+ } else if (tabbedItems.length > 0 && e === tabbedItems[0]) {
+ result.push({
+ itemType: 'tabbed',
+ colCount: 1,
+ colSpan: 1,
+ tabs: tabbedItems.map((tabbedItem: any) => ({
+ title: tabbedItem.caption,
+ colCount: tabbedItem.colCount || 1,
+ items: tabbedItem.items
+ ?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
+ .map(mapFormItem),
+ })),
+ })
+ }
+ })
+
+ return result
+ })()
+ : undefined,
+ }}
+ />
+
+ {/* Columns */}
+ {renderColumns()}
+
+
+ )}
+
+ >
+ )
+}
+
+export default CardView
diff --git a/ui/src/views/list/List.tsx b/ui/src/views/list/List.tsx
index 4e1c3d65..f6f48488 100644
--- a/ui/src/views/list/List.tsx
+++ b/ui/src/views/list/List.tsx
@@ -2,7 +2,7 @@ import { useParams, useSearchParams } from 'react-router-dom'
import { useEffect, useState } from 'react'
import Container from '@/components/shared/Container'
import Grid from './Grid'
-import { FaChartArea, FaList, FaSitemap, FaTable, FaTh, FaUser, FaCalendarAlt } from 'react-icons/fa'
+import { FaChartArea, FaList, FaSitemap, FaTable, FaTh, FaCalendarAlt, FaIdCard, FaProjectDiagram } from 'react-icons/fa'
import { useStoreActions, useStoreState } from '@/store/store'
import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization'
@@ -15,7 +15,7 @@ import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
import { ListViewLayoutType } from '../admin/listForm/edit/types'
import Chart from './Chart'
import Card from './Card'
-import { FaChartGantt } from 'react-icons/fa6'
+import CardView from './CardView'
import GanttView from './GanttView'
import SchedulerView from './SchedulerView'
@@ -124,7 +124,7 @@ const List = () => {
}}
title="Gantt Görünümü"
>
-