diff --git a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/GridColumnOptionDto.cs b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/GridColumnOptionDto.cs index 7f730bf7..b0cd5201 100644 --- a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/GridColumnOptionDto.cs +++ b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/GridColumnOptionDto.cs @@ -20,7 +20,7 @@ public class GridColumnOptionDto /// ilk kolon(lar) sabitlesitirlmesi ayari aktif mi /// - public bool ColumnFixingEnabled { get; set; } + public bool ColumnFixingEnabled { get; set; } = false; /// kolon secme butonu gosterilsin mi /// /// alabilecegi degerler: 'dragAndDrop', 'select' @@ -30,7 +30,7 @@ public class GridColumnOptionDto /// public bool HoverStateEnabled { get; set; } = false; public bool ColumnHidingEnabled { get; set; } = true; - public bool FocusedRowEnabled { get; set; } = false; + public bool FocusedRowEnabled { get; set; } = true; public bool ShowColumnHeaders { get; set; } = true; } diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs index 959b5232..f32d8d34 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs @@ -54,12 +54,13 @@ public static class SeederDefaults public static readonly string DefaultGroupPanelJson = JsonSerializer.Serialize(new { Visible = true }); public static readonly string DefaultColumnOptionJson = JsonSerializer.Serialize(new { - ColumnFixingEnabled = true, + ColumnFixingEnabled = false, ColumnAutoWidth = true, ColumnChooserEnabled = true, AllowColumnResizing = true, AllowColumnReordering = true, ColumnResizingMode = "widget", + FocusRowEnabled = true, }); public static readonly string DefaultLayoutJson = JsonSerializer.Serialize(new LayoutDto() { @@ -72,7 +73,7 @@ public static class SeederDefaults }); public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new SelectionDto { - Mode = GridOptions.SelectionModeSingle, + Mode = GridOptions.SelectionModeNone, AllowSelectAll = false }); public static readonly string DefaultSelectionMultipleJson = JsonSerializer.Serialize(new SelectionDto diff --git a/ui/src/index.css b/ui/src/index.css index 229e86b2..37b5e51a 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -90,3 +90,48 @@ div.dialog-after-open > div.dialog-content.maximized { min-width: fit-content !important; } /* React Pivot Vert Headers cok uzun olmaması icin */ + +/* Grid accessibility status mesajını gizle */ +[e2e-a11y-general-status-container='true'] { + display: none !important; + visibility: hidden !important; + position: absolute !important; + left: -9999px !important; +} +/* Grid accessibility status mesajını gizle */ + +/* Grid Focused Row - Light Mode */ +.dx-datagrid .dx-row-focused.dx-data-row > td:not(.dx-focused), +.dx-datagrid .dx-row-focused.dx-data-row > tr > td:not(.dx-focused) { + background-color: rgba(59, 130, 246, 0.15) !important; + color: inherit !important; +} + +.dx-datagrid .dx-row-focused.dx-data-row .dx-link { + background-color: rgba(255, 255, 255, 0.5) !important; + border-radius: 4px !important; + color: #1e40af !important; +} + +.dx-datagrid .dx-row-focused.dx-data-row .dx-link:hover { + background-color: rgba(255, 255, 255, 0.8) !important; + color: #1e3a8a !important; +} + +/* Grid Focused Row - Dark Mode */ +.dark .dx-datagrid .dx-row-focused.dx-data-row > td:not(.dx-focused), +.dark .dx-datagrid .dx-row-focused.dx-data-row > tr > td:not(.dx-focused) { + background-color: rgba(59, 130, 246, 0.25) !important; + color: #e5e7eb !important; +} + +.dark .dx-datagrid .dx-row-focused.dx-data-row .dx-link { + background-color: rgba(0, 0, 0, 0.3) !important; + color: #93c5fd !important; +} + +.dark .dx-datagrid .dx-row-focused.dx-data-row .dx-link:hover { + background-color: rgba(0, 0, 0, 0.5) !important; + color: #dbeafe !important; +} +/* Grid Focused Row */ \ No newline at end of file diff --git a/ui/src/views/form/FormButtons.tsx b/ui/src/views/form/FormButtons.tsx index 8205e77a..5b84906f 100644 --- a/ui/src/views/form/FormButtons.tsx +++ b/ui/src/views/form/FormButtons.tsx @@ -217,8 +217,7 @@ const FormButtons = (props: { if (onActionNew) { onActionNew() } else { - navigate(ROUTES_ENUM.protected.admin.formNew - .replace(':listFormCode', listFormCode)) + navigate(ROUTES_ENUM.protected.admin.formNew.replace(':listFormCode', listFormCode)) } }} {...(permissions.c ? {} : { disabled: true })} diff --git a/ui/src/views/form/FormDevExpress.tsx b/ui/src/views/form/FormDevExpress.tsx index 9ed1ffa0..fe135506 100644 --- a/ui/src/views/form/FormDevExpress.tsx +++ b/ui/src/views/form/FormDevExpress.tsx @@ -1,6 +1,7 @@ import { DX_CLASSNAMES } from '@/constants/app.constant' import { Form as FormDx, + FormRef, GroupItem as GroupItemDx, Label as LabelDx, SimpleItem as SimpleItemDx, @@ -16,7 +17,7 @@ const FormDevExpress = (props: { listFormCode: string isSubForm?: boolean mode: RowMode - refForm: RefObject + refForm: RefObject formData: any formItems: GroupItem[] setFormData: Dispatch @@ -36,14 +37,14 @@ const FormDevExpress = (props: { // formItems değiştiğinde (özellikle cascading alanlar için) editörlerin dataSource'larını güncelle useEffect(() => { - if (!refForm.current?.instance) return + if (!refForm.current?.instance()) return const allItems = formItems.flatMap((group) => (group.items as SimpleItemWithColData[]) || []) allItems.forEach((item) => { if (item.colData?.lookupDto?.dataSourceType && item.editorOptions?.dataSource) { try { - const editor = refForm.current?.instance.getEditor(item.dataField!) + const editor = refForm.current?.instance().getEditor(item.dataField!) if (editor) { editor.option('dataSource', item.editorOptions.dataSource) } @@ -57,7 +58,7 @@ const FormDevExpress = (props: { // Cascade fieldlerin disabled durumunu güncelle const updateCascadeDisabledStates = () => { - if (!refForm.current?.instance) return + if (!refForm.current?.instance()) return const allItems = formItemsRef.current.flatMap((group) => (group.items as SimpleItemWithColData[]) || []) @@ -67,7 +68,7 @@ const FormDevExpress = (props: { const parentFields = cascadeParentFields.split(',').map((f: string) => f.trim()) try { - const editor = refForm.current?.instance.getEditor(item.dataField!) + const editor = refForm.current?.instance().getEditor(item.dataField!) if (editor && mode !== 'view') { // Parent fieldlerden en az biri boşsa disabled olmalı const shouldDisable = parentFields.some((parentField: string) => { diff --git a/ui/src/views/form/editors/GridBoxEditorComponent.tsx b/ui/src/views/form/editors/GridBoxEditorComponent.tsx index 8edf07cd..7d8a017c 100644 --- a/ui/src/views/form/editors/GridBoxEditorComponent.tsx +++ b/ui/src/views/form/editors/GridBoxEditorComponent.tsx @@ -1,6 +1,6 @@ import { ColumnFormatDto, GridBoxOptionsDto } from '@/proxy/form/models' import { Button } from 'devextreme-react/button' -import DataGrid from 'devextreme-react/data-grid' +import DataGrid, { DataGridRef } from 'devextreme-react/data-grid' import DropDownBox from 'devextreme-react/drop-down-box' import { ReactElement, useRef } from 'react' @@ -19,7 +19,7 @@ const GridBoxEditorComponent = ({ col?: ColumnFormatDto editorOptions: any }): ReactElement => { - const gridRef = useRef(null) + const gridRef = useRef(null) const val = Array.isArray(value) ? value : [value] const lookupDto = col?.lookupDto @@ -43,7 +43,7 @@ const GridBoxEditorComponent = ({ //Eğer clear button kullanılırsa altında grid bilgileride otomatik deselect olmalı ve dropbox kapanmalı if (e.value === null) { e.component.close() - gridRef.current?.instance.deselectAll() + gridRef.current?.instance().deselectAll() } }} contentRender={(e) => ( @@ -100,7 +100,7 @@ const GridBoxEditorComponent = ({ onClick={() => { e.component.option( 'value', - gridRef.current?.instance.getSelectedRowKeys().map((a: any) => a.key), + gridRef.current?.instance().getSelectedRowKeys().map((a: any) => a.key), ) e.component.close() }} diff --git a/ui/src/views/form/useFormData.tsx b/ui/src/views/form/useFormData.tsx index 0cd4cc8d..58dd1472 100644 --- a/ui/src/views/form/useFormData.tsx +++ b/ui/src/views/form/useFormData.tsx @@ -2,7 +2,7 @@ import { Notification, toast } from '@/components/ui' import { getList } from '@/services/form.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { usePermission } from '@/utils/hooks/usePermission' -import { Form as FormDx } from 'devextreme-react/form' +import { FormRef } from 'devextreme-react/form' import { captionize } from 'devextreme/core/utils/inflector' import CustomStore from 'devextreme/data/custom_store' import { GroupItem } from 'devextreme/ui/form' @@ -40,7 +40,7 @@ const useGridData = (props: { const [formItems, setFormItems] = useState([]) const [permissionResults, setPermissionResults] = useState() - const refForm = useRef(null) + const refForm = useRef(null) const [searchParams] = useSearchParams() const navigate = useNavigate() const { translate } = useLocalization() @@ -84,7 +84,7 @@ const useGridData = (props: { return } - const validationResult = refForm.current?.instance.validate() + const validationResult = refForm.current?.instance().validate() if (!validationResult?.isValid) { return } diff --git a/ui/src/views/form/useLookupDataSource.ts b/ui/src/views/form/useLookupDataSource.ts index 31191df8..bf837771 100644 --- a/ui/src/views/form/useLookupDataSource.ts +++ b/ui/src/views/form/useLookupDataSource.ts @@ -1,13 +1,8 @@ -// src/.../useLookupDataSource.ts import { dynamicFetch } from '@/services/form.service' import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models' import CustomStore from 'devextreme/data/custom_store' import { useCallback } from 'react' -/** Module-scope cache: key -> Promise - * - Aynı anda açılan editörler tek ağ çağrısı üzerinden paylaşır - * - Hata olursa cache temizlenir (yeniden denenebilir) - */ const __lookupCache = new Map>() const cachedLoader = (key: string, loader: () => Promise) => { diff --git a/ui/src/views/list/CardItem.tsx b/ui/src/views/list/CardItem.tsx index 11ccc35f..e8b29222 100644 --- a/ui/src/views/list/CardItem.tsx +++ b/ui/src/views/list/CardItem.tsx @@ -5,13 +5,13 @@ import { usePWA } from "@/utils/hooks/usePWA" import CustomStore from "devextreme/data/custom_store" import { useMemo, useRef, useState } from "react" import { useNavigate } from "react-router-dom" -import { Form as FormDx } from 'devextreme-react/form' import { GroupItem } from 'devextreme/ui/form' import { PermissionResults, SimpleItemWithColData } from "../form/types" import { captionize } from 'devextreme/core/utils/inflector' import { ROUTES_ENUM } from "@/routes/route.constant" import FormButtons from "../form/FormButtons" import FormDevExpress from "../form/FormDevExpress" +import { FormRef } from "devextreme-react/cjs/form" const CardItem = ({ isSubForm, @@ -31,7 +31,7 @@ const CardItem = ({ getCachedLookupDataSource: (editorOptions: any, colData: any, row?: any) => any }) => { const [formData, setFormData] = useState(row) - const refForm = useRef(null) + const refForm = useRef(null) const navigate = useNavigate() const { translate } = useLocalization() const { checkPermission } = usePermission() diff --git a/ui/src/views/list/Chart.tsx b/ui/src/views/list/Chart.tsx index ae2b6193..4d1f7191 100644 --- a/ui/src/views/list/Chart.tsx +++ b/ui/src/views/list/Chart.tsx @@ -12,7 +12,7 @@ import { usePermission } from '@/utils/hooks/usePermission' import { Button, toast, Notification } from '@/components/ui' import { ROUTES_ENUM } from '@/routes/route.constant' import { usePWA } from '@/utils/hooks/usePWA' -import { FaCog, FaCrosshairs, FaMinus, FaPlus, FaSearch, FaSyncAlt } from 'react-icons/fa' +import { FaCog, FaCrosshairs, FaSearch, FaSyncAlt } from 'react-icons/fa' import { buildSeriesDto } from './Utils' import { ChartSeriesDto } from '@/proxy/admin/charts/models' import { SelectBoxOption } from '@/types/shared' diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx index 8c59732b..5debffb6 100644 --- a/ui/src/views/list/Grid.tsx +++ b/ui/src/views/list/Grid.tsx @@ -18,6 +18,7 @@ import { Template } from 'devextreme-react/core/template' import DataGrid, { ColumnChooser, ColumnFixing, + DataGridRef, DataGridTypes, Editing, Export, @@ -27,7 +28,6 @@ import DataGrid, { GroupItem as GroupItemDx, GroupPanel, HeaderFilter, - IStateStoringProps, LoadPanel, Pager, Paging, @@ -35,6 +35,7 @@ import DataGrid, { SearchPanel, Selection, Sorting, + StateStoring, Summary, Toolbar, TotalItem, @@ -86,7 +87,7 @@ const Grid = (props: GridProps) => { const { translate } = useLocalization() const { smaller } = useResponsive() - const gridRef = useRef() + const gridRef = useRef() const refListFormCode = useRef('') const widgetGroupRef = useRef(null) @@ -170,7 +171,7 @@ const Grid = (props: GridProps) => { } async function getSelectedRowKeys() { - const grd = gridRef.current?.instance + const grd = gridRef.current?.instance() if (!grd) { return [] } @@ -179,7 +180,7 @@ const Grid = (props: GridProps) => { } function getSelectedRowsData() { - const grd = gridRef.current?.instance + const grd = gridRef.current?.instance() if (!grd) { return [] } @@ -188,11 +189,11 @@ const Grid = (props: GridProps) => { } function refreshData() { - gridRef.current?.instance.refresh() + gridRef.current?.instance()?.refresh() } function getFilter() { - const grd = gridRef.current?.instance + const grd = gridRef.current?.instance() if (!grd) { return } @@ -202,7 +203,7 @@ const Grid = (props: GridProps) => { function onSelectionChanged(data: any) { const grdOpt = gridDto?.gridOptions - const grd = gridRef.current?.instance + const grd = gridRef.current?.instance() if (!grdOpt || !grd) { return } @@ -521,39 +522,41 @@ const Grid = (props: GridProps) => { } const customSaveState = useCallback( - (state: any) => - postListFormCustomization({ + (state: any) => { + return postListFormCustomization({ listFormCode: listFormCode, customizationType: ListFormCustomizationTypeEnum.GridState, - filterName: `list-${gridRef.current?.instance.option('stateStoring')?.storageKey ?? ''}`, + filterName: `list-${gridRef.current?.instance()?.option('stateStoring')?.storageKey ?? ''}`, customizationData: JSON.stringify(state), }).then(() => { setGridPanelColor(statedGridPanelColor) - }), + }) + }, [listFormCode], ) - const customLoadState = useCallback( - () => - getListFormCustomization( - listFormCode, - ListFormCustomizationTypeEnum.GridState, - `list-${gridRef.current?.instance.option('stateStoring')?.storageKey ?? ''}`, - ).then((response: any) => { - setGridPanelColor(statedGridPanelColor) - if (response.data?.length > 0) { - return JSON.parse(response.data[0].customizationData) - } - }), - [listFormCode], - ) + const customLoadState = useCallback(() => { + return getListFormCustomization( + listFormCode, + ListFormCustomizationTypeEnum.GridState, + `list-${gridRef.current?.instance()?.option('stateStoring')?.storageKey ?? ''}`, + ).then((response: any) => { + setGridPanelColor(statedGridPanelColor) + if (response.data?.length > 0) { + return JSON.parse(response.data[0].customizationData) + } + }) + }, [listFormCode]) useEffect(() => { if (gridRef?.current) { - gridRef.current.instance.option('columns', undefined) - gridRef.current.instance.option('remoteOperations', false) - gridRef.current.instance.option('dataSource', undefined) - gridRef.current.instance.state(null) + const instance = gridRef?.current?.instance() + if (instance) { + instance.option('columns', undefined) + instance.option('remoteOperations', false) + instance.option('dataSource', undefined) + instance.state(null) + } } if (refListFormCode.current !== listFormCode) { @@ -644,6 +647,7 @@ const Grid = (props: GridProps) => { layoutTypes.grid, cols, ) + setGridDataSource(dataSource) }, [gridDto, searchParams]) @@ -695,34 +699,12 @@ const Grid = (props: GridProps) => { searchParams?.delete('filter') } - gridRef.current?.instance.refresh() + gridRef.current?.instance()?.refresh() }, [extraFilters]) useEffect(() => { refListFormCode.current = listFormCode - if (!gridRef?.current) { - return - } - - gridRef.current.instance.option('remoteOperations', { groupPaging: true }) - gridRef.current.instance.option('columns', columnData) - gridRef.current.instance.option('dataSource', gridDataSource) - - const stateStoring: IStateStoringProps = { - enabled: gridDto?.gridOptions.stateStoringDto?.enabled, - type: gridDto?.gridOptions.stateStoringDto?.type, - savingTimeout: gridDto?.gridOptions.stateStoringDto?.savingTimeout, - storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey, - } - if ( - gridDto?.gridOptions.stateStoringDto?.enabled && - gridDto?.gridOptions.stateStoringDto?.type === 'custom' - ) { - stateStoring.customSave = customSaveState - stateStoring.customLoad = customLoadState - } - gridRef.current.instance.option('stateStoring', stateStoring) - }, [columnData]) + }, [listFormCode]) // WidgetGroup yüksekliğini hesapla useEffect(() => { @@ -751,7 +733,7 @@ const Grid = (props: GridProps) => { // DevExtreme’in varsayılan export davranışını iptal ediyoruz; kendi akışımızı çalıştıracağız e.cancel = true - const grid = gridRef?.current?.instance + const grid = gridRef?.current?.instance() if (!grid) return try { @@ -767,7 +749,7 @@ const Grid = (props: GridProps) => { const worksheet = workbook.addWorksheet(`${listFormCode}_sheet`) await exportDataExcel({ - component: grid, + component: grid as any, worksheet, autoFilterEnabled: true, }) @@ -798,7 +780,7 @@ const Grid = (props: GridProps) => { await exportDataPdf({ jsPDFDocument: doc, - component: grid, + component: grid as any, indent: 5, }) @@ -829,468 +811,500 @@ const Grid = (props: GridProps) => { defaultTitle="Erp Platform" > )} - {gridDto && columnData && ( - <> -
- 0 - ? gridDto.gridOptions.height - : gridDto.gridOptions.fullHeight - ? `calc(100vh - ${170 + widgetGroupHeight}px)` - : undefined - } - width={gridDto.gridOptions.width || '100%'} - allowColumnResizing={gridDto.gridOptions.columnOptionDto?.allowColumnResizing} - allowColumnReordering={gridDto.gridOptions.columnOptionDto?.allowColumnReordering} - showBorders={gridDto.gridOptions.columnOptionDto?.showBorders} - showRowLines={gridDto.gridOptions.columnOptionDto?.showRowLines} - showColumnLines={gridDto.gridOptions.columnOptionDto?.showColumnLines} - columnResizingMode={gridDto.gridOptions.columnOptionDto?.columnResizingMode} - columnAutoWidth={gridDto.gridOptions.columnOptionDto?.columnAutoWidth} - rtlEnabled={gridDto.gridOptions.columnOptionDto?.rtlEnabled} - rowAlternationEnabled={gridDto.gridOptions.columnOptionDto?.rowAlternationEnabled} - onRowPrepared={(e) => { - //header, filter, data, group, summaries - if (e.rowType === 'data') { - e.rowElement.style.height = gridDto.gridOptions?.rowDto.rowHeight - e.rowElement.style.whiteSpace = gridDto.gridOptions?.rowDto.whiteSpace - e.rowElement.style.overflowWrap = gridDto.gridOptions?.rowDto.overflowWrap + {!gridDto &&
Loading grid configuration...
} + {gridDto && !columnData &&
Loading columns...
} + {gridDto && columnData && !gridDataSource && ( +
Loading data source...
+ )} + {gridDto && + columnData && + gridDataSource && + (() => { + return true + })() && ( + <> +
+ 0 + ? gridDto.gridOptions.height + : gridDto.gridOptions.fullHeight + ? `calc(100vh - ${170 + widgetGroupHeight}px)` + : undefined } - }} - hoverStateEnabled={gridDto.gridOptions.columnOptionDto?.hoverStateEnabled} - columnHidingEnabled={gridDto.gridOptions.columnOptionDto?.columnHidingEnabled} - focusedRowEnabled={gridDto.gridOptions.columnOptionDto?.focusedRowEnabled} - showColumnHeaders={gridDto.gridOptions.columnOptionDto?.showColumnHeaders} - filterSyncEnabled={true} - onSelectionChanged={onSelectionChanged} - onInitNewRow={onInitNewRow} - onCellPrepared={onCellPrepared} - onRowInserting={onRowInserting} - onRowUpdating={onRowUpdating} - onEditingStart={onEditingStart} - onDataErrorOccurred={onDataErrorOccurred} - onExporting={onExporting} - onEditCanceled={() => { - setMode('view') - setIsPopupFullScreen(false) - }} - onSaved={() => { - setMode('view') - setIsPopupFullScreen(false) - }} - onRowInserted={() => { - props.refreshData?.() - }} - onRowUpdated={() => { - props.refreshData?.() - }} - onRowRemoved={() => { - props.refreshData?.() - }} - onEditorPreparing={onEditorPreparing} - > - - { - const grid = gridRef.current?.instance - grid?.saveEditData() - }, - }, - }, - { - widget: 'dxButton', - toolbar: 'bottom', - location: 'after', - options: { - text: translate('::Cancel'), - onClick: () => { - const grid = gridRef.current?.instance - grid?.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), - }, - }, - ], + width={gridDto.gridOptions.width || '100%'} + allowColumnResizing={gridDto.gridOptions.columnOptionDto?.allowColumnResizing} + allowColumnReordering={gridDto.gridOptions.columnOptionDto?.allowColumnReordering} + showBorders={gridDto.gridOptions.columnOptionDto?.showBorders} + showRowLines={gridDto.gridOptions.columnOptionDto?.showRowLines} + showColumnLines={gridDto.gridOptions.columnOptionDto?.showColumnLines} + columnResizingMode={gridDto.gridOptions.columnOptionDto?.columnResizingMode} + columnAutoWidth={gridDto.gridOptions.columnOptionDto?.columnAutoWidth} + rtlEnabled={gridDto.gridOptions.columnOptionDto?.rtlEnabled} + rowAlternationEnabled={gridDto.gridOptions.columnOptionDto?.rowAlternationEnabled} + onRowPrepared={(e) => { + //header, filter, data, group, summaries + if (e.rowType === 'data') { + e.rowElement.style.height = gridDto.gridOptions?.rowDto.rowHeight + e.rowElement.style.whiteSpace = gridDto.gridOptions?.rowDto.whiteSpace + e.rowElement.style.overflowWrap = gridDto.gridOptions?.rowDto.overflowWrap + } }} - 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) + hoverStateEnabled={gridDto.gridOptions.columnOptionDto?.hoverStateEnabled} + columnHidingEnabled={gridDto.gridOptions.columnOptionDto?.columnHidingEnabled} + focusedRowEnabled={gridDto.gridOptions.columnOptionDto?.focusedRowEnabled} + showColumnHeaders={gridDto.gridOptions.columnOptionDto?.showColumnHeaders} + filterSyncEnabled={true} + onSelectionChanged={onSelectionChanged} + onInitNewRow={onInitNewRow} + onCellPrepared={onCellPrepared} + onRowInserting={onRowInserting} + onRowUpdating={onRowUpdating} + onEditingStart={onEditingStart} + onDataErrorOccurred={onDataErrorOccurred} + onExporting={onExporting} + onEditCanceled={() => { + setMode('view') + setIsPopupFullScreen(false) + }} + onSaved={() => { + setMode('view') + setIsPopupFullScreen(false) + }} + onRowInserted={() => { + props.refreshData?.() + }} + onRowUpdated={() => { + props.refreshData?.() + }} + onRowRemoved={() => { + props.refreshData?.() + }} + onEditorPreparing={onEditorPreparing} + > + + + { + const grid = gridRef.current?.instance() + grid?.saveEditData() + }, + }, + }, + { + widget: 'dxButton', + toolbar: 'bottom', + location: 'after', + options: { + text: translate('::Cancel'), + onClick: () => { + const grid = gridRef.current?.instance() + grid?.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)) + }, + 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[] = [] + // 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) + // 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 - }, - ) - } + 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 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, + const hasFilter = filters.some( + ([field, op, val]) => field === i.dataField, ) - if (!existsInExtra) { - editorOptions = { - ...editorOptions, - readOnly: true, + if (hasFilter) { + const existsInExtra = extraFilters.some( + (f) => f.fieldName === i.dataField && !!f.value, + ) + + if (!existsInExtra) { + editorOptions = { + ...editorOptions, + readOnly: true, + } } } } - } - } catch {} + } catch {} - const fieldName = i.dataField.split(':')[0] - const listFormField = gridDto.columnFormats.find( - (x: any) => x.fieldName === fieldName, - ) + 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, + 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, + } } - } 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 } - // Set defaultValue for @AUTONUMBER fields - if ( - typeof listFormField?.defaultValue === 'string' && - listFormField?.defaultValue === '@AUTONUMBER' && - mode === 'new' - ) { - editorOptions = { - ...editorOptions, - value: autoNumber(), - } - } + sortedFormDto.forEach((e: any) => { + if (e.itemType !== 'tabbed') { + // Backend'den gelen colCount ve colSpan değerlerini kullan + const effectiveColCount = e.colCount || 1 + const effectiveColSpan = e.colSpan || 1 - 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, - } + result.push({ + itemType: e.itemType, + colCount: effectiveColCount, + colSpan: effectiveColSpan, + 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 - 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') { - // Backend'den gelen colCount ve colSpan değerlerini kullan - const effectiveColCount = e.colCount || 1 - const effectiveColSpan = e.colSpan || 1 - - result.push({ - itemType: e.itemType, - colCount: effectiveColCount, - colSpan: effectiveColSpan, - 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 + 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 + } + }), } }), - }) - } 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, - }} - > -