2025-12-02 18:15:09 +00:00
|
|
|
|
import Container from '@/components/shared/Container'
|
|
|
|
|
|
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
2025-12-03 22:29:25 +00:00
|
|
|
|
import { GridDto, PlatformEditorTypes, UiLookupDataSourceTypeEnum } from '@/proxy/form/models'
|
2025-12-02 18:15:09 +00:00
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
2025-12-02 20:20:47 +00:00
|
|
|
|
import Scheduler, {
|
|
|
|
|
|
Editing,
|
|
|
|
|
|
Item,
|
|
|
|
|
|
Resource,
|
|
|
|
|
|
SchedulerRef,
|
|
|
|
|
|
Toolbar,
|
|
|
|
|
|
View,
|
|
|
|
|
|
} from 'devextreme-react/scheduler'
|
2025-12-02 18:15:09 +00:00
|
|
|
|
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 { layoutTypes } from '../admin/listForm/edit/types'
|
|
|
|
|
|
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
|
|
|
|
|
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
|
|
|
|
|
import { usePWA } from '@/utils/hooks/usePWA'
|
|
|
|
|
|
import CustomStore from 'devextreme/data/custom_store'
|
|
|
|
|
|
import { Loading } from '@/components/shared'
|
2025-12-02 20:20:47 +00:00
|
|
|
|
import { usePermission } from '@/utils/hooks/usePermission'
|
2025-12-03 22:29:25 +00:00
|
|
|
|
import { useListFormColumns } from './useListFormColumns'
|
|
|
|
|
|
import { EditingFormItemDto } from '@/proxy/form/models'
|
|
|
|
|
|
import useResponsive from '@/utils/hooks/useResponsive'
|
|
|
|
|
|
import { SimpleItemWithColData } from '../form/types'
|
2025-12-02 18:15:09 +00:00
|
|
|
|
|
|
|
|
|
|
interface SchedulerViewProps {
|
|
|
|
|
|
listFormCode: string
|
|
|
|
|
|
searchParams?: URLSearchParams
|
|
|
|
|
|
isSubForm?: boolean
|
|
|
|
|
|
level?: number
|
|
|
|
|
|
refreshData?: () => Promise<void>
|
|
|
|
|
|
gridDto?: GridDto
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const SchedulerView = (props: SchedulerViewProps) => {
|
|
|
|
|
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
|
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
const isPwaMode = usePWA()
|
|
|
|
|
|
|
|
|
|
|
|
const schedulerRef = useRef<SchedulerRef>()
|
|
|
|
|
|
const refListFormCode = useRef('')
|
|
|
|
|
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
2025-12-02 20:20:47 +00:00
|
|
|
|
const { checkPermission } = usePermission()
|
2025-12-03 22:29:25 +00:00
|
|
|
|
const { smaller } = useResponsive()
|
2025-12-02 18:15:09 +00:00
|
|
|
|
|
|
|
|
|
|
const [schedulerDataSource, setSchedulerDataSource] = useState<CustomStore<any, any>>()
|
|
|
|
|
|
const [gridDto, setGridDto] = useState<GridDto>()
|
|
|
|
|
|
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
|
|
|
|
|
const [currentView, setCurrentView] = useState<string>('week')
|
2025-12-03 22:29:25 +00:00
|
|
|
|
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
2025-12-02 18:15:09 +00:00
|
|
|
|
const layout = layoutTypes.scheduler || 'scheduler'
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const initializeScheduler = async () => {
|
|
|
|
|
|
const response = await getList({ listFormCode })
|
|
|
|
|
|
setGridDto(response.data)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (extGridDto === undefined) {
|
|
|
|
|
|
initializeScheduler()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setGridDto(extGridDto)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setCurrentView(extGridDto?.gridOptions.schedulerOptionDto?.defaultView || 'week')
|
|
|
|
|
|
}, [listFormCode, extGridDto])
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (schedulerRef?.current) {
|
|
|
|
|
|
const instance = schedulerRef?.current?.instance()
|
|
|
|
|
|
if (instance) {
|
|
|
|
|
|
instance.option('dataSource', undefined)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (refListFormCode.current !== listFormCode) {
|
|
|
|
|
|
// Reset state if needed
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [listFormCode])
|
|
|
|
|
|
|
|
|
|
|
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef })
|
2025-12-03 22:29:25 +00:00
|
|
|
|
const { getBandedColumns } = useListFormColumns({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
isSubForm,
|
|
|
|
|
|
gridRef: schedulerRef,
|
|
|
|
|
|
})
|
2025-12-02 18:15:09 +00:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!gridDto) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [gridDto])
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!gridDto) return
|
|
|
|
|
|
|
|
|
|
|
|
const dataSource = createSelectDataSource(
|
|
|
|
|
|
gridDto.gridOptions,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
searchParams,
|
|
|
|
|
|
layout,
|
|
|
|
|
|
undefined,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
setSchedulerDataSource(dataSource)
|
|
|
|
|
|
}, [gridDto, searchParams, createSelectDataSource])
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
refListFormCode.current = listFormCode
|
|
|
|
|
|
}, [listFormCode])
|
|
|
|
|
|
|
|
|
|
|
|
// WidgetGroup yüksekliğini hesapla
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const calculateWidgetHeight = () => {
|
|
|
|
|
|
if (widgetGroupRef.current) {
|
|
|
|
|
|
const height = widgetGroupRef.current.offsetHeight
|
|
|
|
|
|
setWidgetGroupHeight(height)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
calculateWidgetHeight()
|
|
|
|
|
|
|
|
|
|
|
|
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
|
|
|
|
|
|
if (widgetGroupRef.current) {
|
|
|
|
|
|
resizeObserver.observe(widgetGroupRef.current)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
resizeObserver.disconnect()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [gridDto?.widgets])
|
|
|
|
|
|
|
|
|
|
|
|
const settingButtonClick = useCallback(() => {
|
|
|
|
|
|
window.open(
|
|
|
|
|
|
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
|
|
|
|
|
|
isPwaMode ? '_self' : '_blank',
|
|
|
|
|
|
)
|
|
|
|
|
|
}, [listFormCode, isPwaMode])
|
|
|
|
|
|
|
2025-12-02 20:20:47 +00:00
|
|
|
|
const handleRefresh = useCallback(() => {
|
|
|
|
|
|
const instance = schedulerRef.current?.instance()
|
|
|
|
|
|
if (instance) {
|
|
|
|
|
|
const dataSource = instance.getDataSource()
|
|
|
|
|
|
if (dataSource) {
|
|
|
|
|
|
dataSource.reload()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2025-12-02 18:15:09 +00:00
|
|
|
|
const onCurrentViewChange = useCallback((value: string) => {
|
|
|
|
|
|
setCurrentView(value)
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2025-12-03 22:29:25 +00:00
|
|
|
|
const onAppointmentFormOpening = useCallback(
|
|
|
|
|
|
(e: any) => {
|
|
|
|
|
|
if (!gridDto) return
|
|
|
|
|
|
|
|
|
|
|
|
// Popup ayarlarını her açılışta yeniden yapılandır
|
|
|
|
|
|
e.popup.option('title', 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('resizeEnabled', gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled)
|
|
|
|
|
|
e.popup.option('fullScreen', isPopupFullScreen)
|
|
|
|
|
|
|
|
|
|
|
|
// Toolbar butonlarını ekle
|
|
|
|
|
|
const toolbarItems = [
|
|
|
|
|
|
{
|
|
|
|
|
|
widget: 'dxButton',
|
|
|
|
|
|
toolbar: 'bottom',
|
|
|
|
|
|
location: 'after',
|
|
|
|
|
|
options: {
|
|
|
|
|
|
text: translate('::Save'),
|
|
|
|
|
|
type: 'default',
|
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
|
const formInstance = e.form
|
|
|
|
|
|
const validationResult = formInstance.validate()
|
|
|
|
|
|
|
|
|
|
|
|
if (validationResult.isValid) {
|
|
|
|
|
|
// Form verilerini al
|
|
|
|
|
|
const formData = formInstance.option('formData')
|
|
|
|
|
|
|
|
|
|
|
|
try {0
|
|
|
|
|
|
// Scheduler instance'ını al
|
|
|
|
|
|
const scheduler = schedulerRef.current?.instance()
|
|
|
|
|
|
|
|
|
|
|
|
if (e.appointmentData && scheduler) {
|
|
|
|
|
|
// Yeni appointment mı yoksa güncelleme mi kontrol et
|
|
|
|
|
|
if (
|
|
|
|
|
|
e.appointmentData.id ||
|
|
|
|
|
|
e.appointmentData[gridDto.gridOptions.keyFieldName || 'id']
|
|
|
|
|
|
) {
|
|
|
|
|
|
// Güncelleme
|
|
|
|
|
|
await scheduler.updateAppointment(e.appointmentData, formData)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Yeni ekleme
|
|
|
|
|
|
await scheduler.addAppointment(formData)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Popup'ı kapat
|
|
|
|
|
|
e.popup.hide()
|
|
|
|
|
|
|
|
|
|
|
|
// RefreshData varsa çağır
|
|
|
|
|
|
if (props.refreshData) {
|
|
|
|
|
|
await props.refreshData()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Appointment save error:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
widget: 'dxButton',
|
|
|
|
|
|
toolbar: 'bottom',
|
|
|
|
|
|
location: 'after',
|
|
|
|
|
|
options: {
|
|
|
|
|
|
text: translate('::Cancel'),
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
e.cancel = true
|
|
|
|
|
|
e.popup.hide()
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
widget: 'dxButton',
|
|
|
|
|
|
toolbar: 'top',
|
|
|
|
|
|
location: 'after',
|
|
|
|
|
|
options: {
|
|
|
|
|
|
icon: 'fullscreen',
|
|
|
|
|
|
hint: translate('::Tam Ekran'),
|
|
|
|
|
|
stylingMode: 'text',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
// 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('toolbarItems', toolbarItems)
|
|
|
|
|
|
|
|
|
|
|
|
// EditingFormDto'dan form items oluştur
|
|
|
|
|
|
const formItems: any[] = []
|
|
|
|
|
|
|
|
|
|
|
|
if (gridDto.gridOptions.editingFormDto?.length > 0) {
|
|
|
|
|
|
const sortedFormDto = gridDto.gridOptions.editingFormDto
|
|
|
|
|
|
.slice()
|
|
|
|
|
|
.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
|
|
|
|
|
|
|
|
|
|
|
sortedFormDto.forEach((group: any) => {
|
2025-12-03 22:45:18 +00:00
|
|
|
|
// Items'ları da order'a göre sırala
|
|
|
|
|
|
const sortedItems = (group.items || [])
|
|
|
|
|
|
.slice()
|
|
|
|
|
|
.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
|
|
|
|
|
|
|
|
|
|
|
const groupItems = sortedItems.map((i: EditingFormItemDto) => {
|
2025-12-03 22:29:25 +00:00
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
// EditorType belirleme - Grid'deki gibi
|
|
|
|
|
|
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) {
|
|
|
|
|
|
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',
|
|
|
|
|
|
loadMode: 'raw',
|
|
|
|
|
|
load: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { dynamicFetch } = await import('@/services/form.service')
|
|
|
|
|
|
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
listFormFieldName: fieldName,
|
|
|
|
|
|
filters: [],
|
|
|
|
|
|
})
|
|
|
|
|
|
return (response.data ?? []).map((a: any) => ({
|
|
|
|
|
|
key: a.Key,
|
|
|
|
|
|
name: a.Name,
|
|
|
|
|
|
group: a.Group,
|
|
|
|
|
|
}))
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Lookup load error:', error)
|
|
|
|
|
|
return []
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
editorOptions.valueExpr = 'key'
|
|
|
|
|
|
editorOptions.displayExpr = 'name'
|
|
|
|
|
|
} else if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.StaticData) {
|
|
|
|
|
|
if (lookup.lookupQuery) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const staticData = JSON.parse(lookup.lookupQuery)
|
|
|
|
|
|
editorOptions.dataSource = staticData
|
|
|
|
|
|
editorOptions.valueExpr = lookup.valueExpr || 'key'
|
|
|
|
|
|
editorOptions.displayExpr = lookup.displayExpr || 'name'
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Static data parse error:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validation rules
|
|
|
|
|
|
const validationRules: any[] = []
|
|
|
|
|
|
if (i.isRequired) {
|
|
|
|
|
|
validationRules.push({ type: 'required' })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:45:18 +00:00
|
|
|
|
const item: any = {
|
2025-12-03 22:29:25 +00:00
|
|
|
|
dataField: i.dataField,
|
2025-12-03 22:45:18 +00:00
|
|
|
|
name: i.dataField,
|
2025-12-03 22:29:25 +00:00
|
|
|
|
editorType,
|
|
|
|
|
|
colSpan: i.colSpan,
|
|
|
|
|
|
editorOptions,
|
|
|
|
|
|
validationRules: validationRules.length > 0 ? validationRules : undefined,
|
|
|
|
|
|
}
|
2025-12-03 22:45:18 +00:00
|
|
|
|
|
|
|
|
|
|
// Label sadece caption varsa ekle
|
|
|
|
|
|
if (listFormField?.captionName) {
|
|
|
|
|
|
item.label = { text: translate('::' + listFormField.captionName) }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return item
|
2025-12-03 22:29:25 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (group.itemType === 'group') {
|
2025-12-03 22:45:18 +00:00
|
|
|
|
// Grup kullanmadan direkt items'ları ekle - form'un colCount'u geçerli olsun
|
|
|
|
|
|
formItems.push(...groupItems)
|
2025-12-03 22:29:25 +00:00
|
|
|
|
} else if (group.itemType === 'tabbed') {
|
|
|
|
|
|
formItems.push({
|
|
|
|
|
|
itemType: 'tabbed',
|
2025-12-03 22:45:18 +00:00
|
|
|
|
tabs: (group.tabs || []).map((tab: any) => {
|
|
|
|
|
|
// Tab items'larını da order'a göre sırala
|
|
|
|
|
|
const sortedTabItems = (tab.items || [])
|
|
|
|
|
|
.slice()
|
|
|
|
|
|
.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1))
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
title: translate('::' + tab.title),
|
|
|
|
|
|
colCount: tab.colCount || 2,
|
|
|
|
|
|
items: sortedTabItems.map((i: EditingFormItemDto) => {
|
2025-12-03 22:29:25 +00:00
|
|
|
|
// Tab içindeki itemlar için de aynı mapping
|
|
|
|
|
|
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,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// EditorType belirleme - Grid'deki gibi
|
|
|
|
|
|
let editorType: any = i.editorType2 || i.editorType
|
|
|
|
|
|
if (i.editorType2 === PlatformEditorTypes.dxGridBox) {
|
|
|
|
|
|
editorType = 'dxDropDownBox'
|
|
|
|
|
|
} else if (i.editorType2) {
|
|
|
|
|
|
editorType = i.editorType2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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',
|
|
|
|
|
|
loadMode: 'raw',
|
|
|
|
|
|
load: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { dynamicFetch } = await import('@/services/form.service')
|
|
|
|
|
|
const response = await dynamicFetch(
|
|
|
|
|
|
'list-form-select/lookup',
|
|
|
|
|
|
'POST',
|
|
|
|
|
|
null,
|
|
|
|
|
|
{
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
listFormFieldName: fieldName,
|
|
|
|
|
|
filters: [],
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
return (response.data ?? []).map((a: any) => ({
|
|
|
|
|
|
key: a.Key,
|
|
|
|
|
|
name: a.Name,
|
|
|
|
|
|
group: a.Group,
|
|
|
|
|
|
}))
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Lookup load error:', error)
|
|
|
|
|
|
return []
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
editorOptions.valueExpr = 'key'
|
|
|
|
|
|
editorOptions.displayExpr = 'name'
|
|
|
|
|
|
} else if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.StaticData) {
|
|
|
|
|
|
if (lookup.lookupQuery) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const staticData = JSON.parse(lookup.lookupQuery)
|
|
|
|
|
|
editorOptions.dataSource = staticData
|
|
|
|
|
|
editorOptions.valueExpr = lookup.valueExpr || 'key'
|
|
|
|
|
|
editorOptions.displayExpr = lookup.displayExpr || 'name'
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Static data parse error:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const validationRules: any[] = []
|
|
|
|
|
|
if (i.isRequired) {
|
|
|
|
|
|
validationRules.push({ type: 'required' })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:45:18 +00:00
|
|
|
|
const item: any = {
|
2025-12-03 22:29:25 +00:00
|
|
|
|
dataField: i.dataField,
|
2025-12-03 22:45:18 +00:00
|
|
|
|
name: i.dataField,
|
2025-12-03 22:29:25 +00:00
|
|
|
|
editorType,
|
|
|
|
|
|
colSpan: i.colSpan,
|
|
|
|
|
|
editorOptions,
|
|
|
|
|
|
validationRules: validationRules.length > 0 ? validationRules : undefined,
|
|
|
|
|
|
}
|
2025-12-03 22:45:18 +00:00
|
|
|
|
|
|
|
|
|
|
// Label sadece caption varsa ekle
|
|
|
|
|
|
if (listFormField?.captionName) {
|
|
|
|
|
|
item.label = { text: translate('::' + listFormField.captionName) }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return item
|
2025-12-03 22:29:25 +00:00
|
|
|
|
}),
|
2025-12-03 22:45:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
}),
|
2025-12-03 22:29:25 +00:00
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// No group, add items directly
|
|
|
|
|
|
formItems.push(...groupItems)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:45:18 +00:00
|
|
|
|
// Form'u tamamen yeniden yapılandır
|
|
|
|
|
|
const formConfig = {
|
|
|
|
|
|
colCount: gridDto.gridOptions.editingFormDto?.[0]?.colCount || 2,
|
|
|
|
|
|
showValidationSummary: false,
|
|
|
|
|
|
items: formItems,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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()
|
2025-12-03 22:29:25 +00:00
|
|
|
|
},
|
|
|
|
|
|
[gridDto, translate, isPopupFullScreen, listFormCode],
|
|
|
|
|
|
)
|
2025-12-02 18:15:09 +00:00
|
|
|
|
|
|
|
|
|
|
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"
|
2025-12-02 20:20:47 +00:00
|
|
|
|
></Helmet>
|
2025-12-02 18:15:09 +00:00
|
|
|
|
)}
|
|
|
|
|
|
{!gridDto && (
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
<Loading loading>Loading scheduler configuration...</Loading>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{gridDto && !schedulerDataSource && (
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
<Loading loading>Loading data source...</Loading>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{gridDto && schedulerDataSource && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<div className="p-1">
|
|
|
|
|
|
<Scheduler
|
|
|
|
|
|
ref={schedulerRef as any}
|
|
|
|
|
|
key={`Scheduler-${listFormCode}-${schedulerDataSource ? 'loaded' : 'loading'}`}
|
|
|
|
|
|
id={'Scheduler-' + listFormCode}
|
|
|
|
|
|
dataSource={schedulerDataSource}
|
|
|
|
|
|
textExpr={gridDto.gridOptions.schedulerOptionDto?.textExpr || 'text'}
|
|
|
|
|
|
startDateExpr={gridDto.gridOptions.schedulerOptionDto?.startDateExpr || 'startDate'}
|
|
|
|
|
|
endDateExpr={gridDto.gridOptions.schedulerOptionDto?.endDateExpr || 'endDate'}
|
|
|
|
|
|
allDayExpr={gridDto.gridOptions.schedulerOptionDto?.allDayExpr}
|
|
|
|
|
|
recurrenceRuleExpr={gridDto.gridOptions.schedulerOptionDto?.recurrenceRuleExpr}
|
|
|
|
|
|
recurrenceExceptionExpr={
|
|
|
|
|
|
gridDto.gridOptions.schedulerOptionDto?.recurrenceExceptionExpr
|
|
|
|
|
|
}
|
|
|
|
|
|
startDayHour={gridDto.gridOptions.schedulerOptionDto?.startDayHour || 8}
|
|
|
|
|
|
endDayHour={gridDto.gridOptions.schedulerOptionDto?.endDayHour || 18}
|
|
|
|
|
|
currentView={currentView}
|
|
|
|
|
|
onCurrentViewChange={onCurrentViewChange}
|
|
|
|
|
|
onAppointmentFormOpening={onAppointmentFormOpening}
|
2025-12-03 21:01:00 +00:00
|
|
|
|
onAppointmentAdding={() => {
|
|
|
|
|
|
props.refreshData?.()
|
|
|
|
|
|
}}
|
|
|
|
|
|
onAppointmentUpdating={() => {
|
|
|
|
|
|
props.refreshData?.()
|
|
|
|
|
|
}}
|
|
|
|
|
|
onAppointmentDeleting={() => {
|
|
|
|
|
|
props.refreshData?.()
|
|
|
|
|
|
}}
|
2025-12-02 18:15:09 +00:00
|
|
|
|
height={
|
|
|
|
|
|
gridDto.gridOptions.height > 0
|
|
|
|
|
|
? gridDto.gridOptions.height
|
|
|
|
|
|
: gridDto.gridOptions.fullHeight
|
|
|
|
|
|
? `calc(100vh - ${170 + widgetGroupHeight}px)`
|
|
|
|
|
|
: undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
showAllDayPanel={gridDto.gridOptions.schedulerOptionDto?.showAllDayPanel ?? true}
|
|
|
|
|
|
crossScrollingEnabled={
|
|
|
|
|
|
gridDto.gridOptions.schedulerOptionDto?.crossScrollingEnabled ?? false
|
|
|
|
|
|
}
|
|
|
|
|
|
cellDuration={gridDto.gridOptions.schedulerOptionDto?.cellDuration || 30}
|
|
|
|
|
|
firstDayOfWeek={
|
|
|
|
|
|
(gridDto.gridOptions.schedulerOptionDto?.firstDayOfWeek as
|
|
|
|
|
|
| 0
|
|
|
|
|
|
| 1
|
|
|
|
|
|
| 2
|
|
|
|
|
|
| 3
|
|
|
|
|
|
| 4
|
|
|
|
|
|
| 5
|
|
|
|
|
|
| 6) || 1
|
|
|
|
|
|
}
|
|
|
|
|
|
adaptivityEnabled={true}
|
|
|
|
|
|
>
|
2025-12-02 20:20:47 +00:00
|
|
|
|
<Toolbar>
|
|
|
|
|
|
<Item name="dateNavigator" />
|
|
|
|
|
|
<Item name="today" />
|
|
|
|
|
|
<Item name="viewSwitcher" />
|
|
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
options={{
|
|
|
|
|
|
icon: 'refresh',
|
|
|
|
|
|
hint: translate('::ListForms.ListForm.Refresh'),
|
|
|
|
|
|
onClick: handleRefresh,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
|
|
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
options={{
|
|
|
|
|
|
icon: 'preferences',
|
|
|
|
|
|
hint: 'Settings',
|
|
|
|
|
|
onClick: settingButtonClick,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Toolbar>
|
|
|
|
|
|
|
2025-12-02 18:15:09 +00:00
|
|
|
|
<Editing
|
2025-12-02 20:54:56 +00:00
|
|
|
|
allowAdding={gridDto.gridOptions.schedulerOptionDto?.allowAdding ?? false}
|
|
|
|
|
|
allowUpdating={gridDto.gridOptions.schedulerOptionDto?.allowUpdating ?? false}
|
|
|
|
|
|
allowDeleting={gridDto.gridOptions.schedulerOptionDto?.allowDeleting ?? false}
|
2025-12-02 18:15:09 +00:00
|
|
|
|
allowResizing={gridDto.gridOptions.schedulerOptionDto?.allowResizing ?? false}
|
|
|
|
|
|
allowDragging={gridDto.gridOptions.schedulerOptionDto?.allowDragging ?? false}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2025-12-02 19:58:31 +00:00
|
|
|
|
<View type="day" name={translate('::ListForms.SchedulerOptions.Day')} />
|
|
|
|
|
|
<View type="week" name={translate('::ListForms.SchedulerOptions.Week')} />
|
|
|
|
|
|
<View type="workWeek" name={translate('::ListForms.SchedulerOptions.WorkWeek')} />
|
|
|
|
|
|
<View type="month" name={translate('::ListForms.SchedulerOptions.Month')} />
|
2025-12-02 18:15:09 +00:00
|
|
|
|
<View
|
|
|
|
|
|
type="timelineDay"
|
2025-12-02 19:58:31 +00:00
|
|
|
|
name={translate('::ListForms.SchedulerOptions.TimelineDay')}
|
2025-12-02 18:15:09 +00:00
|
|
|
|
maxAppointmentsPerCell="unlimited"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<View
|
|
|
|
|
|
type="timelineWeek"
|
2025-12-02 19:58:31 +00:00
|
|
|
|
name={translate('::ListForms.SchedulerOptions.TimelineWeek')}
|
2025-12-02 18:15:09 +00:00
|
|
|
|
maxAppointmentsPerCell="unlimited"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<View
|
|
|
|
|
|
type="timelineMonth"
|
2025-12-02 19:58:31 +00:00
|
|
|
|
name={translate('::ListForms.SchedulerOptions.TimelineMonth')}
|
2025-12-02 18:15:09 +00:00
|
|
|
|
maxAppointmentsPerCell="unlimited"
|
|
|
|
|
|
/>
|
2025-12-02 19:58:31 +00:00
|
|
|
|
<View type="agenda" name={translate('::ListForms.SchedulerOptions.Agenda')} />
|
2025-12-02 18:15:09 +00:00
|
|
|
|
|
|
|
|
|
|
{gridDto.gridOptions.schedulerOptionDto?.resources?.map((resource, index) => (
|
|
|
|
|
|
<Resource
|
|
|
|
|
|
key={index}
|
|
|
|
|
|
fieldExpr={resource.fieldExpr}
|
|
|
|
|
|
dataSource={resource.dataSource}
|
|
|
|
|
|
label={resource.label}
|
|
|
|
|
|
useColorAsDefault={resource.useColorAsDefault}
|
|
|
|
|
|
/>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Scheduler>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Container>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default SchedulerView
|