2026-02-24 20:44:16 +00:00
|
|
|
|
import Container from '@/components/shared/Container'
|
2026-08-13 08:41:48 +00:00
|
|
|
|
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
2026-07-29 07:57:49 +00:00
|
|
|
|
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import Gantt, {
|
|
|
|
|
|
Column,
|
|
|
|
|
|
ContextMenu,
|
|
|
|
|
|
Editing,
|
|
|
|
|
|
FilterRow,
|
2026-07-13 08:39:13 +00:00
|
|
|
|
type GanttRef,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
HeaderFilter,
|
|
|
|
|
|
Item,
|
|
|
|
|
|
Sorting,
|
|
|
|
|
|
Tasks,
|
|
|
|
|
|
Toolbar,
|
|
|
|
|
|
Validation,
|
|
|
|
|
|
} from 'devextreme-react/gantt'
|
|
|
|
|
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
2026-08-13 08:41:48 +00:00
|
|
|
|
import PageTitle from '@/components/shared/PageTitle'
|
|
|
|
|
|
import { useStylesheet } from '@/utils/hooks/useStylesheet'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
|
|
|
|
|
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'
|
2026-07-29 07:57:49 +00:00
|
|
|
|
import type { ContentReadyEvent, GanttScaleType } from 'devextreme/ui/gantt'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useListFormColumns } from './useListFormColumns'
|
|
|
|
|
|
import CustomStore from 'devextreme/data/custom_store'
|
|
|
|
|
|
import { GridColumnData } from './GridColumnData'
|
|
|
|
|
|
import { Loading } from '@/components/shared'
|
|
|
|
|
|
import { usePermission } from '@/utils/hooks/usePermission'
|
2026-07-29 07:57:49 +00:00
|
|
|
|
import { useListFormStateStoring } from './useListFormStateStoring'
|
|
|
|
|
|
import TreeList from 'devextreme/ui/tree_list'
|
|
|
|
|
|
import { postListFormCustomization } from '@/services/list-form-customization.service'
|
|
|
|
|
|
import { useFilters } from './useFilters'
|
2026-08-06 22:54:50 +00:00
|
|
|
|
import {
|
|
|
|
|
|
useListFormCustomSources,
|
|
|
|
|
|
useListFormGridDto,
|
|
|
|
|
|
useWidgetGroupHeight,
|
|
|
|
|
|
} from './shared/hooks'
|
2026-07-29 07:57:49 +00:00
|
|
|
|
import GridFilterDialogs from './GridFilterDialogs'
|
|
|
|
|
|
|
|
|
|
|
|
type GanttInstance = ReturnType<GanttRef['instance']>
|
2026-08-05 20:51:43 +00:00
|
|
|
|
type GanttTreeListInstance = {
|
|
|
|
|
|
element: () => HTMLElement
|
|
|
|
|
|
getVisibleColumns: () => Array<Record<string, any>>
|
|
|
|
|
|
state: {
|
|
|
|
|
|
(): Record<string, any>
|
|
|
|
|
|
(value: Record<string, any> | null): void
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-29 07:57:49 +00:00
|
|
|
|
|
|
|
|
|
|
const getGanttTreeList = (gantt: GanttInstance) => {
|
|
|
|
|
|
const element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
2026-08-05 20:51:43 +00:00
|
|
|
|
return element
|
|
|
|
|
|
? (TreeList.getInstance(element as HTMLElement) as unknown as GanttTreeListInstance)
|
|
|
|
|
|
: undefined
|
2026-07-29 07:57:49 +00:00
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
interface GanttViewProps {
|
|
|
|
|
|
listFormCode: string
|
|
|
|
|
|
searchParams?: URLSearchParams
|
|
|
|
|
|
isSubForm?: boolean
|
|
|
|
|
|
level?: number
|
|
|
|
|
|
refreshData?: () => Promise<void>
|
|
|
|
|
|
gridDto?: GridDto
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const GanttView = (props: GanttViewProps) => {
|
2026-07-06 09:44:26 +00:00
|
|
|
|
const { listFormCode, searchParams, isSubForm, gridDto: extGridDto } = props
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
const isPwaMode = usePWA()
|
|
|
|
|
|
|
2026-08-13 08:41:48 +00:00
|
|
|
|
// Eskiden Helmet'in <link> çocuğuydu; koşul aynen korunuyor.
|
|
|
|
|
|
useStylesheet(isSubForm ? null : '/css/gantt/dx-gantt.min.css', 'dx-gantt-css')
|
|
|
|
|
|
|
|
|
|
|
|
const gridRef = useRef<GanttRef | undefined>(undefined)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
|
const { checkPermission } = usePermission()
|
|
|
|
|
|
|
|
|
|
|
|
const [ganttDataSource, setGanttDataSource] = useState<CustomStore<any, any>>()
|
|
|
|
|
|
const [columnData, setColumnData] = useState<GridColumnData[]>()
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const gridDto = useListFormGridDto(listFormCode, extGridDto)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const [scaleType, setScaleType] = useState<GanttScaleType>('weeks')
|
2026-07-29 07:57:49 +00:00
|
|
|
|
const [taskListWidth, setTaskListWidth] = useState(500)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const layout = layoutTypes.gantt || 'gantt'
|
2026-08-13 08:41:48 +00:00
|
|
|
|
const pendingStateRef = useRef<Record<string, any> | undefined>(undefined)
|
2026-07-29 07:57:49 +00:00
|
|
|
|
const loadedStateKeyRef = useRef('')
|
|
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, gridDto?.widgets)
|
|
|
|
|
|
useListFormCustomSources(gridDto)
|
|
|
|
|
|
|
2026-07-29 07:57:49 +00:00
|
|
|
|
const { customSaveState, customLoadState, storageKey } = useListFormStateStoring({
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
|
|
|
|
|
|
filterPrefix: 'gantt',
|
|
|
|
|
|
})
|
|
|
|
|
|
const ganttFilterRef = useRef<any>({
|
|
|
|
|
|
instance: () => {
|
|
|
|
|
|
const gantt = gridRef.current?.instance()
|
|
|
|
|
|
return gantt ? getGanttTreeList(gantt) : undefined
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
const { filterToolbarData, ...filterData } = useFilters({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
gridRef: ganttFilterRef,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
})
|
|
|
|
|
|
const ganttFilterToolbarData = filterToolbarData.filter((item) =>
|
|
|
|
|
|
['saveActiveFilter', 'deleteFilter', 'clearFilter'].includes(item.name ?? ''),
|
|
|
|
|
|
)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
setScaleType(gridDto?.gridOptions.ganttOptionDto?.scaleType || 'weeks')
|
|
|
|
|
|
}, [gridDto])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-08-06 22:54:50 +00:00
|
|
|
|
// listFormCode değişiminde eski veri kaynağını temizle.
|
2026-02-24 20:44:16 +00:00
|
|
|
|
useEffect(() => {
|
2026-08-06 22:54:50 +00:00
|
|
|
|
gridRef.current?.instance()?.option('dataSource', undefined)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}, [listFormCode])
|
|
|
|
|
|
|
|
|
|
|
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
|
|
|
|
|
const { getBandedColumns } = useListFormColumns({
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
isSubForm,
|
|
|
|
|
|
gridRef,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!gridDto) return
|
|
|
|
|
|
|
|
|
|
|
|
const cols = getBandedColumns()
|
|
|
|
|
|
setColumnData(cols)
|
|
|
|
|
|
|
|
|
|
|
|
const dataSource = createSelectDataSource(
|
|
|
|
|
|
gridDto.gridOptions,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
searchParams,
|
|
|
|
|
|
layout,
|
|
|
|
|
|
undefined,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
setGanttDataSource(dataSource)
|
2026-08-05 20:51:43 +00:00
|
|
|
|
}, [createSelectDataSource, getBandedColumns, gridDto, layout, listFormCode, searchParams])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const settingButtonClick = useCallback(() => {
|
|
|
|
|
|
window.open(
|
|
|
|
|
|
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
|
|
|
|
|
|
isPwaMode ? '_self' : '_blank',
|
|
|
|
|
|
)
|
2026-08-05 20:51:43 +00:00
|
|
|
|
}, [isPwaMode, listFormCode])
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const getSettingButtonOptions = useCallback(
|
|
|
|
|
|
() => ({
|
|
|
|
|
|
icon: 'preferences',
|
2026-07-29 07:57:49 +00:00
|
|
|
|
text: translate('::ListForms.ListForm.Manage'),
|
|
|
|
|
|
hint: translate('::ListForms.ListForm.Manage'),
|
2026-02-24 20:44:16 +00:00
|
|
|
|
stylingMode: 'icon',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
settingButtonClick()
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
2026-07-29 07:57:49 +00:00
|
|
|
|
[settingButtonClick, translate],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const getRefreshButtonOptions = useCallback(
|
|
|
|
|
|
() => ({
|
|
|
|
|
|
icon: 'refresh',
|
|
|
|
|
|
text: translate('::ListForms.ListForm.Refresh'),
|
|
|
|
|
|
stylingMode: 'icon',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
gridRef.current?.instance()?.refresh()
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
2026-08-05 20:51:43 +00:00
|
|
|
|
[translate],
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-07-29 07:57:49 +00:00
|
|
|
|
const getCurrentState = useCallback((gantt: GanttInstance) => {
|
|
|
|
|
|
const treeList = getGanttTreeList(gantt)
|
|
|
|
|
|
const state = (treeList?.state() ?? {}) as Record<string, any>
|
|
|
|
|
|
const visibleColumns = (treeList?.getVisibleColumns() ?? []) as Array<Record<string, any>>
|
|
|
|
|
|
const headerCells = Array.from(
|
|
|
|
|
|
(treeList?.element() as HTMLElement | undefined)?.querySelectorAll('.dx-header-row > td') ??
|
|
|
|
|
|
[],
|
|
|
|
|
|
)
|
|
|
|
|
|
const widths = new Map(
|
|
|
|
|
|
visibleColumns.map((column, index) => [
|
|
|
|
|
|
column.dataField,
|
|
|
|
|
|
headerCells[index]?.getBoundingClientRect().width,
|
|
|
|
|
|
]),
|
|
|
|
|
|
)
|
|
|
|
|
|
const taskListElement = (gantt.element() as HTMLElement).querySelector(
|
|
|
|
|
|
'.dx-gantt-treelist-wrapper',
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
...state,
|
|
|
|
|
|
columns: state.columns?.map((column: Record<string, any>) => {
|
|
|
|
|
|
const width = widths.get(column.dataField)
|
|
|
|
|
|
return width ? { ...column, width: Math.round(width) } : column
|
|
|
|
|
|
}),
|
|
|
|
|
|
scaleType: gantt.option('scaleType'),
|
|
|
|
|
|
showDependencies: gantt.option('showDependencies'),
|
|
|
|
|
|
showResources: gantt.option('showResources'),
|
|
|
|
|
|
taskListWidth: Math.round(
|
2026-08-05 20:51:43 +00:00
|
|
|
|
taskListElement?.getBoundingClientRect().width ?? gantt.option('taskListWidth') ?? 500,
|
2026-07-29 07:57:49 +00:00
|
|
|
|
),
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const applyState = useCallback((gantt: GanttInstance, state: Record<string, any>) => {
|
|
|
|
|
|
const treeList = getGanttTreeList(gantt)
|
|
|
|
|
|
if (!treeList) return
|
|
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
|
scaleType: savedScaleType,
|
|
|
|
|
|
showDependencies,
|
|
|
|
|
|
showResources,
|
|
|
|
|
|
taskListWidth: savedTaskListWidth,
|
|
|
|
|
|
treeListState,
|
|
|
|
|
|
...treeState
|
|
|
|
|
|
} = state
|
|
|
|
|
|
|
|
|
|
|
|
treeList.state(treeListState ?? treeState)
|
|
|
|
|
|
if (savedScaleType) setScaleType(savedScaleType)
|
|
|
|
|
|
if (savedTaskListWidth) setTaskListWidth(savedTaskListWidth)
|
|
|
|
|
|
gantt.option({
|
|
|
|
|
|
...(showDependencies !== undefined ? { showDependencies } : {}),
|
|
|
|
|
|
...(showResources !== undefined ? { showResources } : {}),
|
|
|
|
|
|
...(savedTaskListWidth ? { taskListWidth: savedTaskListWidth } : {}),
|
|
|
|
|
|
})
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const handleGanttContentReady = useCallback(
|
|
|
|
|
|
(event: ContentReadyEvent) => {
|
|
|
|
|
|
if (!pendingStateRef.current) return
|
|
|
|
|
|
|
|
|
|
|
|
const state = pendingStateRef.current
|
|
|
|
|
|
pendingStateRef.current = undefined
|
|
|
|
|
|
requestAnimationFrame(() => requestAnimationFrame(() => applyState(event.component, state)))
|
|
|
|
|
|
},
|
|
|
|
|
|
[applyState],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const saveGanttState = useCallback(() => {
|
|
|
|
|
|
const gantt = gridRef.current?.instance()
|
|
|
|
|
|
if (gantt) customSaveState(getCurrentState(gantt)).catch(() => undefined)
|
|
|
|
|
|
}, [customSaveState, getCurrentState])
|
|
|
|
|
|
|
|
|
|
|
|
const resetGanttState = useCallback(async () => {
|
|
|
|
|
|
const gantt = gridRef.current?.instance()
|
|
|
|
|
|
if (!gantt || !storageKey) return
|
|
|
|
|
|
|
|
|
|
|
|
await postListFormCustomization({
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
customizationType: ListFormCustomizationTypeEnum.GridState,
|
|
|
|
|
|
filterName: `gantt-${storageKey}`,
|
|
|
|
|
|
customizationData: '',
|
|
|
|
|
|
})
|
|
|
|
|
|
getGanttTreeList(gantt)?.state(null)
|
|
|
|
|
|
setTaskListWidth(500)
|
|
|
|
|
|
}, [listFormCode, storageKey])
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!gridDto?.gridOptions.stateStoringDto?.enabled ||
|
|
|
|
|
|
!ganttDataSource ||
|
|
|
|
|
|
!columnData ||
|
|
|
|
|
|
!storageKey
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
const key = `${listFormCode}-${storageKey}`
|
|
|
|
|
|
if (loadedStateKeyRef.current === key) return
|
|
|
|
|
|
loadedStateKeyRef.current = key
|
|
|
|
|
|
|
|
|
|
|
|
customLoadState()
|
|
|
|
|
|
.then((state) => {
|
|
|
|
|
|
pendingStateRef.current = state ?? undefined
|
|
|
|
|
|
const gantt = gridRef.current?.instance()
|
|
|
|
|
|
if (gantt && state) handleGanttContentReady({ component: gantt } as ContentReadyEvent)
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
loadedStateKeyRef.current = ''
|
|
|
|
|
|
})
|
|
|
|
|
|
}, [
|
|
|
|
|
|
columnData,
|
|
|
|
|
|
customLoadState,
|
|
|
|
|
|
ganttDataSource,
|
|
|
|
|
|
gridDto,
|
|
|
|
|
|
handleGanttContentReady,
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
storageKey,
|
|
|
|
|
|
])
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<>
|
2026-06-18 14:03:22 +00:00
|
|
|
|
<div ref={widgetGroupRef} className={(gridDto?.widgets?.length ?? 0) > 0 ? 'mt-2' : ''}>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<Container className={DX_CLASSNAMES}>
|
2026-08-13 08:41:48 +00:00
|
|
|
|
{!isSubForm && <PageTitle title={translate('::' + gridDto?.gridOptions.title)} />}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{!gridDto && (
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
<Loading loading>Loading gantt configuration...</Loading>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{gridDto && !ganttDataSource && (
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
<Loading loading>Loading data source...</Loading>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{gridDto && columnData && ganttDataSource && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<div className="p-1">
|
|
|
|
|
|
<Gantt
|
|
|
|
|
|
ref={gridRef as any}
|
|
|
|
|
|
key={`Gantt-${listFormCode}-${ganttDataSource ? 'loaded' : 'loading'}`}
|
|
|
|
|
|
id={'Gantt-' + listFormCode}
|
2026-07-30 14:37:15 +00:00
|
|
|
|
className="list-view-toolbar-host"
|
2026-07-29 07:57:49 +00:00
|
|
|
|
taskListWidth={taskListWidth}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
scaleType={scaleType}
|
|
|
|
|
|
rootValue={
|
|
|
|
|
|
gridDto.gridOptions.ganttOptionDto?.rootValue === '' ||
|
|
|
|
|
|
gridDto.gridOptions.ganttOptionDto?.rootValue === undefined
|
|
|
|
|
|
? null
|
|
|
|
|
|
: gridDto.gridOptions.ganttOptionDto?.rootValue
|
|
|
|
|
|
}
|
|
|
|
|
|
height={
|
|
|
|
|
|
gridDto.gridOptions.height > 0
|
|
|
|
|
|
? gridDto.gridOptions.height
|
|
|
|
|
|
: gridDto.gridOptions.fullHeight
|
|
|
|
|
|
? `calc(100vh - ${170 + widgetGroupHeight}px)`
|
|
|
|
|
|
: undefined
|
|
|
|
|
|
}
|
2026-07-29 07:57:49 +00:00
|
|
|
|
onContentReady={handleGanttContentReady}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
>
|
|
|
|
|
|
<Tasks
|
|
|
|
|
|
dataSource={ganttDataSource}
|
|
|
|
|
|
keyExpr={gridDto.gridOptions.ganttOptionDto?.keyExpr}
|
|
|
|
|
|
parentIdExpr={gridDto.gridOptions.ganttOptionDto?.parentIdExpr}
|
|
|
|
|
|
titleExpr={gridDto.gridOptions.ganttOptionDto?.titleExpr}
|
|
|
|
|
|
startExpr={gridDto.gridOptions.ganttOptionDto?.startExpr}
|
|
|
|
|
|
endExpr={gridDto.gridOptions.ganttOptionDto?.endExpr}
|
|
|
|
|
|
progressExpr={gridDto.gridOptions.ganttOptionDto?.progressExpr}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-06-10 14:56:00 +00:00
|
|
|
|
<Toolbar>
|
2026-06-10 07:53:32 +00:00
|
|
|
|
<Item name="undo" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="redo" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="separator" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="collapseAll" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="expandAll" locateInMenu="auto" />
|
|
|
|
|
|
{gridDto.gridOptions.ganttOptionDto?.allowTaskAdding && (
|
|
|
|
|
|
<Item name="addTask" locateInMenu="auto" />
|
|
|
|
|
|
)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{gridDto.gridOptions.ganttOptionDto?.allowTaskDeleting && (
|
2026-06-10 07:53:32 +00:00
|
|
|
|
<Item name="deleteTask" locateInMenu="auto" />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)}
|
2026-06-10 07:53:32 +00:00
|
|
|
|
<Item name="separator" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="zoomIn" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="zoomOut" locateInMenu="auto" />
|
|
|
|
|
|
<Item name="separator" locateInMenu="auto" />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxSelectBox"
|
2026-06-10 07:53:32 +00:00
|
|
|
|
locateInMenu="auto"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
options={{
|
|
|
|
|
|
width: 150,
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ value: 'auto', text: translate('::Auto') },
|
|
|
|
|
|
{ value: 'minutes', text: translate('::Minutes') },
|
|
|
|
|
|
{ value: 'hours', text: translate('::Hours') },
|
|
|
|
|
|
{ value: 'days', text: translate('::Days') },
|
|
|
|
|
|
{ value: 'weeks', text: translate('::Weeks') },
|
|
|
|
|
|
{ value: 'months', text: translate('::Months') },
|
|
|
|
|
|
{ value: 'quarters', text: translate('::Quarters') },
|
|
|
|
|
|
{ value: 'years', text: translate('::Years') },
|
|
|
|
|
|
],
|
|
|
|
|
|
displayExpr: 'text',
|
|
|
|
|
|
valueExpr: 'value',
|
|
|
|
|
|
value: scaleType,
|
|
|
|
|
|
onValueChanged: (e: any) => setScaleType(e.value),
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-06-10 07:53:32 +00:00
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
locateInMenu="auto"
|
|
|
|
|
|
showText="always"
|
|
|
|
|
|
options={getRefreshButtonOptions()}
|
|
|
|
|
|
/>
|
2026-07-29 07:57:49 +00:00
|
|
|
|
{ganttFilterToolbarData.map((item) => (
|
|
|
|
|
|
<Item
|
|
|
|
|
|
key={item.name}
|
|
|
|
|
|
name={item.name}
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
locateInMenu="never"
|
|
|
|
|
|
showText="always"
|
|
|
|
|
|
options={item.options}
|
|
|
|
|
|
/>
|
|
|
|
|
|
))}
|
|
|
|
|
|
{gridDto.gridOptions.stateStoringDto?.enabled && (
|
|
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
locateInMenu="auto"
|
|
|
|
|
|
showText="always"
|
|
|
|
|
|
options={{
|
|
|
|
|
|
icon: 'save',
|
|
|
|
|
|
text: translate('::ListForms.ListForm.SaveGridState'),
|
|
|
|
|
|
hint: translate('::ListForms.ListForm.SaveGridState'),
|
|
|
|
|
|
onClick: saveGanttState,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{gridDto.gridOptions.stateStoringDto?.enabled && (
|
|
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
locateInMenu="auto"
|
|
|
|
|
|
showText="always"
|
|
|
|
|
|
options={{
|
|
|
|
|
|
icon: 'revert',
|
|
|
|
|
|
text: translate('::ListForms.ListForm.ResetGridState'),
|
|
|
|
|
|
hint: translate('::ListForms.ListForm.ResetGridState'),
|
|
|
|
|
|
onClick: resetGanttState,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-06-19 14:15:46 +00:00
|
|
|
|
{checkPermission('App.Listforms.Listform.Update') && (
|
2026-06-10 07:53:32 +00:00
|
|
|
|
<Item
|
|
|
|
|
|
location="after"
|
|
|
|
|
|
widget="dxButton"
|
|
|
|
|
|
locateInMenu="auto"
|
|
|
|
|
|
showText="always"
|
|
|
|
|
|
options={getSettingButtonOptions()}
|
|
|
|
|
|
/>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
)}
|
|
|
|
|
|
</Toolbar>
|
2026-07-29 07:57:49 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<Editing
|
|
|
|
|
|
enabled={gridDto.gridOptions.ganttOptionDto?.allowEditing}
|
|
|
|
|
|
allowTaskAdding={gridDto.gridOptions.ganttOptionDto?.allowTaskAdding}
|
|
|
|
|
|
allowTaskUpdating={gridDto.gridOptions.ganttOptionDto?.allowTaskUpdating}
|
|
|
|
|
|
allowTaskDeleting={gridDto.gridOptions.ganttOptionDto?.allowTaskDeleting}
|
2026-07-29 07:57:49 +00:00
|
|
|
|
allowTaskResourceUpdating={
|
|
|
|
|
|
gridDto.gridOptions.ganttOptionDto?.allowTaskResourceUpdating
|
|
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
allowDependencyAdding={gridDto.gridOptions.ganttOptionDto?.allowDependencyAdding}
|
|
|
|
|
|
allowDependencyDeleting={
|
|
|
|
|
|
gridDto.gridOptions.ganttOptionDto?.allowDependencyDeleting
|
|
|
|
|
|
}
|
|
|
|
|
|
allowResourceAdding={gridDto.gridOptions.ganttOptionDto?.allowResourceAdding}
|
|
|
|
|
|
allowResourceDeleting={gridDto.gridOptions.ganttOptionDto?.allowResourceDeleting}
|
2026-07-29 07:57:49 +00:00
|
|
|
|
allowResourceUpdating={gridDto.gridOptions.ganttOptionDto?.allowResourceUpdating}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
2026-08-05 20:51:43 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<FilterRow visible={gridDto.gridOptions.filterRowDto?.visible}></FilterRow>
|
|
|
|
|
|
<HeaderFilter visible={gridDto.gridOptions.headerFilterDto.visible}></HeaderFilter>
|
|
|
|
|
|
<Sorting mode={gridDto.gridOptions?.sortMode}></Sorting>
|
|
|
|
|
|
<Validation autoUpdateParentTasks={true} />
|
|
|
|
|
|
<ContextMenu enabled={false} />
|
|
|
|
|
|
|
|
|
|
|
|
{columnData
|
|
|
|
|
|
.filter((col) => col.type != 'buttons')
|
|
|
|
|
|
.map((col: any) => (
|
|
|
|
|
|
<Column key={col.dataField} {...col} />
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Gantt>
|
2026-07-29 07:57:49 +00:00
|
|
|
|
<GridFilterDialogs
|
|
|
|
|
|
gridRef={ganttFilterRef}
|
|
|
|
|
|
listFormCode={listFormCode}
|
|
|
|
|
|
{...filterData}
|
|
|
|
|
|
/>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Container>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default GanttView
|