From 32720abac2f12a59087365f9bcead30fdf623dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Thu, 4 Dec 2025 10:33:27 +0300 Subject: [PATCH] =?UTF-8?q?SchedulerView=20mode=20ve=20tabbed=20style=20d?= =?UTF-8?q?=C3=BCzenlemesi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Seeds/ListFormSeeder_Maintenance.cs | 4 +- ui/src/views/list/SchedulerView.tsx | 234 +++++++++++------- 2 files changed, 151 insertions(+), 87 deletions(-) diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Maintenance.cs b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Maintenance.cs index e6314bbf..184bc876 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Maintenance.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Maintenance.cs @@ -2801,11 +2801,11 @@ public class ListFormSeeder_Maintenance : IDataSeedContributor, ITransientDepend DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson, PagerOptionJson = DefaultPagerOptionJson, InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson, - EditingOptionJson = DefaultEditingOptionJson(listFormName, 800, 450, true, true, true, true, false), + EditingOptionJson = DefaultEditingOptionJson(listFormName, 800, 500, true, true, true, true, false), EditingFormJson = JsonSerializer.Serialize(new List() { new() { Order=1, ColCount=2, ColSpan=1, ItemType="group", Items= [ - new EditingFormItemDto { Order = 1, DataField = "Subject", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextArea }, + new EditingFormItemDto { Order = 1, DataField = "Subject", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox }, new EditingFormItemDto { Order = 2, DataField = "WorkcenterId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, new EditingFormItemDto { Order = 3, DataField = "WorkorderTypeId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, new EditingFormItemDto { Order = 4, DataField = "Priority", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, diff --git a/ui/src/views/list/SchedulerView.tsx b/ui/src/views/list/SchedulerView.tsx index 49b197b4..de66cf13 100644 --- a/ui/src/views/list/SchedulerView.tsx +++ b/ui/src/views/list/SchedulerView.tsx @@ -1,6 +1,11 @@ import Container from '@/components/shared/Container' import { DX_CLASSNAMES } from '@/constants/app.constant' -import { GridDto, PlatformEditorTypes, UiLookupDataSourceTypeEnum } from '@/proxy/form/models' +import { + DbTypeEnum, + GridDto, + PlatformEditorTypes, + UiLookupDataSourceTypeEnum, +} from '@/proxy/form/models' import { useLocalization } from '@/utils/hooks/useLocalization' import Scheduler, { Editing, @@ -14,7 +19,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { Helmet } from 'react-helmet' import { getList } from '@/services/form.service' import { useListFormCustomDataSource } from './useListFormCustomDataSource' -import { addCss, addJs } from './Utils' +import { addCss, addJs, autoNumber } from './Utils' import { layoutTypes } from '../admin/listForm/edit/types' import WidgetGroup from '@/components/ui/Widget/WidgetGroup' import { ROUTES_ENUM } from '@/routes/route.constant' @@ -22,10 +27,10 @@ import { usePWA } from '@/utils/hooks/usePWA' import CustomStore from 'devextreme/data/custom_store' import { Loading } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' -import { useListFormColumns } from './useListFormColumns' import { EditingFormItemDto } from '@/proxy/form/models' -import useResponsive from '@/utils/hooks/useResponsive' -import { colSpan } from '@/components/ui/Widget/iconList' +import { RowMode, SimpleItemWithColData } from '../form/types' +import { captionize } from 'devextreme/core/utils/inflector' +import { AppointmentFormOpeningEvent } from 'devextreme/ui/scheduler' interface SchedulerViewProps { listFormCode: string @@ -40,6 +45,7 @@ const SchedulerView = (props: SchedulerViewProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props const { translate } = useLocalization() const isPwaMode = usePWA() + const [mode, setMode] = useState('view') const schedulerRef = useRef() const refListFormCode = useRef('') @@ -82,12 +88,6 @@ const SchedulerView = (props: SchedulerViewProps) => { }, [listFormCode]) const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef }) - const { getBandedColumns } = useListFormColumns({ - gridDto, - listFormCode, - isSubForm, - gridRef: schedulerRef, - }) useEffect(() => { if (!gridDto) { @@ -169,19 +169,31 @@ const SchedulerView = (props: SchedulerViewProps) => { }, []) const onAppointmentFormOpening = useCallback( - (e: any) => { + (e: AppointmentFormOpeningEvent) => { if (!gridDto) return + // Yeni appointment mı yoksa düzenleme mi kontrol et + const isNewAppointment = + !e.appointmentData || + (!e.appointmentData.id && !e.appointmentData[gridDto.gridOptions.keyFieldName || 'id']) + + const currentMode = isNewAppointment ? 'new' : 'edit' + setMode(currentMode) + // Popup ayarlarını her açılışta yeniden yapılandır - e.popup.option('title', translate('::' + gridDto.gridOptions.editingOptionDto?.popup?.title)) + e.popup.option( + 'title', + (currentMode === 'new' ? '✚ ' : '🖊️ ') + + translate('::' + gridDto.gridOptions.editingOptionDto?.popup?.title), + ) e.popup.option('showTitle', gridDto.gridOptions.editingOptionDto?.popup?.showTitle) - e.popup.option('width', gridDto.gridOptions.editingOptionDto?.popup?.width || 800) - e.popup.option('height', gridDto.gridOptions.editingOptionDto?.popup?.height || 600) + e.popup.option('width', gridDto.gridOptions.editingOptionDto?.popup?.width) + e.popup.option('height', gridDto.gridOptions.editingOptionDto?.popup?.height) e.popup.option('resizeEnabled', gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled) e.popup.option('fullScreen', isPopupFullScreen) // Toolbar butonlarını ekle - const toolbarItems = [ + e.popup.option('toolbarItems', [ { widget: 'dxButton', toolbar: 'bottom', @@ -197,7 +209,7 @@ const SchedulerView = (props: SchedulerViewProps) => { // Form verilerini al const formData = formInstance.option('formData') - try {0 + try { // Scheduler instance'ını al const scheduler = schedulerRef.current?.instance() @@ -253,40 +265,31 @@ const SchedulerView = (props: SchedulerViewProps) => { // Popup'tan mevcut fullScreen durumunu al const currentFullScreen = e.popup.option('fullScreen') const newFullScreenState = !currentFullScreen - + // State'i güncelle setIsPopupFullScreen(newFullScreenState) - + // Popup'ı güncelle e.popup.option('fullScreen', newFullScreenState) - + // Width ve height'i da güncelle if (newFullScreenState) { e.popup.option('width', '100%') e.popup.option('height', '100%') } else { e.popup.option('width', gridDto.gridOptions.editingOptionDto?.popup?.width || 600) - e.popup.option('height', gridDto.gridOptions.editingOptionDto?.popup?.height || 'auto') - } - - // Button icon ve hint'i güncelle - const button = e.popup._$element.find('.dx-toolbar-after .dx-button').last() - if (button.length) { - const buttonInstance = button.dxButton('instance') - if (buttonInstance) { - buttonInstance.option('icon', newFullScreenState ? 'collapse' : 'fullscreen') - buttonInstance.option('hint', newFullScreenState ? translate('::Normal Boyut') : translate('::Tam Ekran')) - } + e.popup.option( + 'height', + gridDto.gridOptions.editingOptionDto?.popup?.height || 'auto', + ) } }, }, }, - ] - - e.popup.option('toolbarItems', toolbarItems) + ]) // EditingFormDto'dan form items oluştur - const formItems: any[] = [] + const result: any[] = [] if (gridDto.gridOptions.editingFormDto?.length > 0) { const sortedFormDto = gridDto.gridOptions.editingFormDto @@ -308,6 +311,42 @@ const SchedulerView = (props: SchedulerViewProps) => { 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' && + currentMode === 'new' + ) { + editorOptions = { + ...editorOptions, + value: autoNumber(), + } + } + // EditorType belirleme let editorType: any = i.editorType2 || i.editorType if (i.editorType2 === PlatformEditorTypes.dxGridBox) { @@ -320,11 +359,6 @@ const SchedulerView = (props: SchedulerViewProps) => { if (listFormField?.lookupDto) { const lookup = listFormField.lookupDto - // EditorType'ı dxSelectBox olarak ayarla - if (!editorType || editorType === 'dxTextBox') { - editorType = 'dxSelectBox' - } - if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.Query) { editorOptions.dataSource = new CustomStore({ key: 'key', @@ -365,81 +399,111 @@ const SchedulerView = (props: SchedulerViewProps) => { } } - // Validation rules - const validationRules: any[] = [] - if (i.isRequired) { - validationRules.push({ type: 'required' }) - } - - const item: any = { + 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, - editorType, + editorType2: i.editorType2, + editorType: + i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2, colSpan: i.colSpan, + isRequired: i.isRequired, editorOptions, - validationRules: validationRules.length > 0 ? validationRules : undefined, + editorScript: i.editorScript, } - // Label sadece caption varsa ekle - if (listFormField?.captionName) { - item.label = { text: translate('::' + listFormField.captionName) } + if (i.dataField.indexOf(':') >= 0) { + item.label = { text: captionize(i.dataField.split(':')[1]) } + } + + if ((currentMode == 'edit' && !item.canUpdate) || (currentMode == 'new' && !item.canCreate)) { + item.editorOptions = { + ...item.editorOptions, + readOnly: true, + } } return item } - sortedFormDto.forEach((group: any) => { + sortedFormDto.forEach((e: any) => { // Items'ları da order'a göre sırala - const sortedItems = (group.items || []) + const sortedItems = (e.items || []) .slice() .sort((a: any, b: any) => (a.order >= b.order ? 1 : -1)) - + const groupItems = sortedItems.map(mapFormItem) - if (group.itemType === 'group') { + if (e.itemType !== 'tabbed') { // Grup kullanmadan direkt items'ları ekle - form'un colCount'u geçerli olsun - formItems.push(...groupItems) - } else if (group.itemType === 'tabbed' && tabbedItems.length > 0 && group === tabbedItems[0]) { - // Tüm tabbed items'ları tek bir tabbed item olarak ekle (Grid'deki gibi) - formItems.push({ + result.push(...groupItems) + } else if (e.itemType === 'tabbed' && tabbedItems.length > 0 && e === tabbedItems[0]) { + // Tabbed için caption OLMAMALI - sadece tabs array içinde title kullan + result.push({ itemType: 'tabbed', - // colCount ve colSpan kaldırıldı - tab'ın tüm genişliği kullanması için + // colCount tabbed item için OLMAMALI - sadece colSpan + colSpan: 1, + // caption kullanma! Tabs içindeki title'lar yeterli tabs: tabbedItems.map((tabbedItem: any) => { - // Tab items'larını order'a göre sırala - const sortedTabItems = (tabbedItem.items || []) - .slice() - .sort((a: any, b: any) => (a.order >= b.order ? 1 : -1)) - + // Backend'den gelen colCount ve colSpan değerlerini kullan + const effectiveColCount = tabbedItem.colCount || 2 + return { - title: translate('::' + tabbedItem.caption), // Backend'den caption geliyor - colCount: tabbedItem.colCount || 2, // Tab içindeki sütun sayısı - items: sortedTabItems.map(mapFormItem), + 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 (currentMode === 'new') { + return a.canCreate || a.canRead + } else if (currentMode === 'edit') { + return a.canUpdate || a.canRead + } + return false + }), } }), }) - } else { - // No group, add items directly - formItems.push(...groupItems) } }) } // Form'u tamamen yeniden yapılandır - // Tabbed varsa form colCount 1, yoksa backend'den gelen değer - const hasTabbedItems = formItems.some((item: any) => item.itemType === 'tabbed') - const formConfig = { - colCount: hasTabbedItems ? 1 : (gridDto.gridOptions.editingFormDto?.[0]?.colCount || 2), - showValidationSummary: false, - items: formItems, + const hasTabbedItems = result.some((item: any) => item.itemType === 'tabbed') + + // Form'u tamamen yeniden baştan yapılandır + e.form.option( + 'colCount', + hasTabbedItems ? 1 : gridDto.gridOptions.editingFormDto?.[0]?.colCount || 2, + ) + e.form.option( + 'colCount', + hasTabbedItems ? 1 : gridDto.gridOptions.editingFormDto?.[0]?.colCount || 2, + ) + e.form.option('showValidationSummary', false) + e.form.option('items', result) + + // Yeni versiyonlarda bu şekilde Style değiştirme işlemi yapmak zorunda olmayabiliriz. + // Yani geçici olarak eklenmiştir. + // Tabbed varsa belirli class'lara sahip elementi bul ve flex-direction'ı değiştir + if (hasTabbedItems) { + setTimeout(() => { + const targetElement = document.querySelector('.dx-item-content.dx-box-item-content.dx-box-flex.dx-box.dx-widget.dx-collection') + if (targetElement) { + const htmlElement = targetElement as HTMLElement + + // Mevcut style'ı al ve flex-direction: row'u column'a çevir + const currentStyle = htmlElement.getAttribute('style') || '' + const updatedStyle = currentStyle.replace(/flex-direction:\s*row/gi, 'flex-direction: column') + htmlElement.setAttribute('style', updatedStyle) + } + }, 100) } - // Form'u tamamen yeniden baştan yapılandır - e.form.option('colCount', formConfig.colCount) - e.form.option('showValidationSummary', formConfig.showValidationSummary) - e.form.option('items', formConfig.items) - - // Form'u repaint et - e.form.repaint() }, [gridDto, translate, isPopupFullScreen, listFormCode], )