318 lines
9.6 KiB
TypeScript
318 lines
9.6 KiB
TypeScript
|
|
import Container from '@/components/shared/Container'
|
|||
|
|
import { Dialog, Notification, toast } from '@/components/ui'
|
|||
|
|
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
|||
|
|
import { GridDto } from '@/proxy/form/models'
|
|||
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
|
|
import useResponsive from '@/utils/hooks/useResponsive'
|
|||
|
|
import Gantt, {
|
|||
|
|
Column,
|
|||
|
|
Dependencies,
|
|||
|
|
Editing,
|
|||
|
|
GanttRef,
|
|||
|
|
GanttTypes,
|
|||
|
|
ResourceAssignments,
|
|||
|
|
Resources,
|
|||
|
|
Tasks,
|
|||
|
|
Validation,
|
|||
|
|
} from 'devextreme-react/gantt'
|
|||
|
|
import { Button } from '@/components/ui'
|
|||
|
|
import CustomStore from 'devextreme/data/custom_store'
|
|||
|
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|||
|
|
import { Helmet } from 'react-helmet'
|
|||
|
|
import { getList } from '@/services/form.service'
|
|||
|
|
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
|||
|
|
import { useToolbar } from './useToolbar'
|
|||
|
|
import { useFilters } from './useFilters'
|
|||
|
|
import { addCss, addJs } from './Utils'
|
|||
|
|
import { layoutTypes } from '../admin/listForm/edit/types'
|
|||
|
|
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
|
|||
|
|
|
|||
|
|
interface GanttViewProps {
|
|||
|
|
listFormCode: string
|
|||
|
|
searchParams?: URLSearchParams
|
|||
|
|
isSubForm?: boolean
|
|||
|
|
level?: number
|
|||
|
|
refreshData?: () => Promise<void>
|
|||
|
|
gridDto?: GridDto
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const GanttView = (props: GanttViewProps) => {
|
|||
|
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
|||
|
|
const { translate } = useLocalization()
|
|||
|
|
const { smaller } = useResponsive()
|
|||
|
|
|
|||
|
|
const ganttRef = useRef<GanttRef>()
|
|||
|
|
const refListFormCode = useRef('')
|
|||
|
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
|||
|
|
|
|||
|
|
const [ganttDataSource, setGanttDataSource] = useState<CustomStore<any, any>>()
|
|||
|
|
const [gridDto, setGridDto] = useState<GridDto>()
|
|||
|
|
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const initializeGantt = async () => {
|
|||
|
|
const response = await getList({ listFormCode })
|
|||
|
|
setGridDto(response.data)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (extGridDto === undefined) {
|
|||
|
|
initializeGantt()
|
|||
|
|
} else {
|
|||
|
|
setGridDto(extGridDto)
|
|||
|
|
}
|
|||
|
|
}, [listFormCode, extGridDto])
|
|||
|
|
|
|||
|
|
const layout = layoutTypes.gantt || 'gantt'
|
|||
|
|
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
|
|||
|
|
gridDto,
|
|||
|
|
listFormCode,
|
|||
|
|
getSelectedRowKeys: () => Promise.resolve([]),
|
|||
|
|
getSelectedRowsData: () => [],
|
|||
|
|
refreshData,
|
|||
|
|
getFilter: () => undefined,
|
|||
|
|
layout,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const { filterToolbarData, ...filterData } = useFilters({
|
|||
|
|
gridDto,
|
|||
|
|
gridRef: ganttRef as any,
|
|||
|
|
listFormCode,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: ganttRef as any })
|
|||
|
|
|
|||
|
|
function refreshData() {
|
|||
|
|
ganttRef.current?.instance()?.refresh()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onTaskInserted() {
|
|||
|
|
props.refreshData?.()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onTaskUpdated() {
|
|||
|
|
props.refreshData?.()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onTaskDeleted() {
|
|||
|
|
props.refreshData?.()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (ganttRef?.current) {
|
|||
|
|
const instance = ganttRef?.current?.instance()
|
|||
|
|
if (instance) {
|
|||
|
|
instance.option('dataSource', undefined)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (refListFormCode.current !== listFormCode) {
|
|||
|
|
// Reset state if needed
|
|||
|
|
}
|
|||
|
|
}, [listFormCode])
|
|||
|
|
|
|||
|
|
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,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
setGanttDataSource(dataSource)
|
|||
|
|
}, [gridDto, searchParams])
|
|||
|
|
|
|||
|
|
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])
|
|||
|
|
|
|||
|
|
// Gantt için sütunları oluştur
|
|||
|
|
const getGanttColumns = useCallback(() => {
|
|||
|
|
if (!gridDto?.columnFormats) return []
|
|||
|
|
|
|||
|
|
return gridDto.columnFormats
|
|||
|
|
.filter((col) => col.canRead && col.isActive && col.visible)
|
|||
|
|
.map((col) => {
|
|||
|
|
const column: any = {
|
|||
|
|
dataField: col.fieldName,
|
|||
|
|
caption: col.captionName ? translate('::' + col.captionName) : col.fieldName,
|
|||
|
|
width: col.width > 0 ? col.width : undefined,
|
|||
|
|
alignment: col.alignment,
|
|||
|
|
format: col.format,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return column
|
|||
|
|
})
|
|||
|
|
}, [gridDto, translate])
|
|||
|
|
|
|||
|
|
const ganttColumns = getGanttColumns()
|
|||
|
|
|
|||
|
|
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"
|
|||
|
|
></Helmet>
|
|||
|
|
)}
|
|||
|
|
{!gridDto && <div className="p-4">Loading gantt configuration...</div>}
|
|||
|
|
{gridDto && !ganttDataSource && <div className="p-4">Loading data source...</div>}
|
|||
|
|
{gridDto && ganttDataSource && (
|
|||
|
|
<>
|
|||
|
|
{/* Custom Toolbar */}
|
|||
|
|
{(toolbarData.length > 0 || filterToolbarData.length > 0) && (
|
|||
|
|
<div className="flex flex-wrap gap-2 p-2 border-b bg-white">
|
|||
|
|
{toolbarData.map((item: any) => {
|
|||
|
|
if (item.widget === 'dxButton' && item.options) {
|
|||
|
|
return (
|
|||
|
|
<Button
|
|||
|
|
key={item.name}
|
|||
|
|
size="sm"
|
|||
|
|
variant={item.options.type || 'solid'}
|
|||
|
|
icon={item.options.icon}
|
|||
|
|
onClick={item.options.onClick}
|
|||
|
|
disabled={item.options.disabled}
|
|||
|
|
>
|
|||
|
|
{item.options.text}
|
|||
|
|
</Button>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
return null
|
|||
|
|
})}
|
|||
|
|
{filterToolbarData.map((item: any) => {
|
|||
|
|
if (item.widget === 'dxButton' && item.options) {
|
|||
|
|
return (
|
|||
|
|
<Button
|
|||
|
|
key={item.name}
|
|||
|
|
size="sm"
|
|||
|
|
variant={item.options.type || 'solid'}
|
|||
|
|
icon={item.options.icon}
|
|||
|
|
onClick={item.options.onClick}
|
|||
|
|
disabled={item.options.disabled}
|
|||
|
|
>
|
|||
|
|
{item.options.text}
|
|||
|
|
</Button>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
return null
|
|||
|
|
})}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<div className="p-1">
|
|||
|
|
<Gantt
|
|||
|
|
ref={ganttRef as any}
|
|||
|
|
key={`Gantt-${listFormCode}-${ganttDataSource ? 'loaded' : 'loading'}`}
|
|||
|
|
id={'Gantt-' + listFormCode}
|
|||
|
|
taskListWidth={500}
|
|||
|
|
scaleType="weeks"
|
|||
|
|
height={
|
|||
|
|
gridDto.gridOptions.height > 0
|
|||
|
|
? gridDto.gridOptions.height
|
|||
|
|
: gridDto.gridOptions.fullHeight
|
|||
|
|
? `calc(100vh - ${170 + widgetGroupHeight}px)`
|
|||
|
|
: 700
|
|||
|
|
}
|
|||
|
|
showResources={true}
|
|||
|
|
showDependencies={true}
|
|||
|
|
onTaskInserted={onTaskInserted}
|
|||
|
|
onTaskUpdated={onTaskUpdated}
|
|||
|
|
onTaskDeleted={onTaskDeleted}
|
|||
|
|
>
|
|||
|
|
<Tasks dataSource={ganttDataSource} />
|
|||
|
|
<Dependencies dataSource={[]} />
|
|||
|
|
<Resources dataSource={[]} />
|
|||
|
|
<ResourceAssignments dataSource={[]} />
|
|||
|
|
|
|||
|
|
<Editing
|
|||
|
|
enabled={
|
|||
|
|
gridDto.gridOptions.editingOptionDto?.allowAdding ||
|
|||
|
|
gridDto.gridOptions.editingOptionDto?.allowUpdating ||
|
|||
|
|
gridDto.gridOptions.editingOptionDto?.allowDeleting
|
|||
|
|
}
|
|||
|
|
allowTaskAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
|||
|
|
allowTaskUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
|||
|
|
allowTaskDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
|||
|
|
allowDependencyAdding={false}
|
|||
|
|
allowDependencyDeleting={false}
|
|||
|
|
allowResourceAdding={false}
|
|||
|
|
allowResourceDeleting={false}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<Validation autoUpdateParentTasks={true} />
|
|||
|
|
|
|||
|
|
{ganttColumns.map((col: any) => (
|
|||
|
|
<Column
|
|||
|
|
key={col.dataField}
|
|||
|
|
dataField={col.dataField}
|
|||
|
|
caption={col.caption}
|
|||
|
|
width={col.width}
|
|||
|
|
/>
|
|||
|
|
))}
|
|||
|
|
</Gantt>
|
|||
|
|
</div>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<Dialog
|
|||
|
|
isOpen={toolbarModalData?.open || false}
|
|||
|
|
onClose={() => setToolbarModalData(undefined)}
|
|||
|
|
onRequestClose={() => setToolbarModalData(undefined)}
|
|||
|
|
>
|
|||
|
|
{toolbarModalData?.content}
|
|||
|
|
</Dialog>
|
|||
|
|
</Container>
|
|||
|
|
</>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default GanttView
|