erp-platform/ui/src/views/list/GanttView.tsx
2025-12-02 23:54:56 +03:00

321 lines
11 KiB
TypeScript

import Container from '@/components/shared/Container'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import { GridDto } from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import Gantt, {
Column,
ContextMenu,
Editing,
FilterRow,
GanttRef,
HeaderFilter,
Item,
Sorting,
Tasks,
Toolbar,
Validation,
} from 'devextreme-react/gantt'
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 { GanttScaleType } from 'devextreme/ui/gantt'
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'
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 isPwaMode = usePWA()
const gridRef = useRef<GanttRef>()
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const { checkPermission } = usePermission()
const [ganttDataSource, setGanttDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>()
const [gridDto, setGridDto] = useState<GridDto>()
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const [scaleType, setScaleType] = useState<GanttScaleType>('weeks')
const layout = layoutTypes.gantt || 'gantt'
useEffect(() => {
const initializeGantt = async () => {
const response = await getList({ listFormCode })
setGridDto(response.data)
}
if (extGridDto === undefined) {
initializeGantt()
} else {
setGridDto(extGridDto)
}
setScaleType(extGridDto?.gridOptions.ganttOptionDto?.scaleType || 'weeks')
}, [listFormCode, extGridDto])
useEffect(() => {
if (gridRef?.current) {
const instance = gridRef?.current?.instance()
if (instance) {
instance.option('dataSource', undefined)
}
}
if (refListFormCode.current !== listFormCode) {
// Reset state if needed
}
}, [listFormCode])
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
const { getBandedColumns } = useListFormColumns({
gridDto,
listFormCode,
isSubForm,
gridRef,
})
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 cols = getBandedColumns()
setColumnData(cols)
const dataSource = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layout,
undefined,
)
setGanttDataSource(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',
)
}, [])
const getSettingButtonOptions = useCallback(
() => ({
icon: 'preferences',
stylingMode: 'icon',
onClick: () => {
settingButtonClick()
},
}),
[settingButtonClick],
)
const getRefreshButtonOptions = useCallback(
() => ({
icon: 'refresh',
text: translate('::ListForms.ListForm.Refresh'),
stylingMode: 'icon',
onClick: () => {
gridRef.current?.instance()?.refresh()
},
}),
[settingButtonClick],
)
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"
>
<link rel="stylesheet" href="/css/gantt/dx-gantt.min.css" />
</Helmet>
)}
{!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}
taskListWidth={500}
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
}
>
<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}
/>
<Toolbar>
<Item name="undo" />
<Item name="redo" />
<Item name="separator" />
<Item name="collapseAll" />
<Item name="expandAll" />
{gridDto.gridOptions.ganttOptionDto?.allowTaskAdding && <Item name="addTask" />}
{gridDto.gridOptions.ganttOptionDto?.allowTaskDeleting && (
<Item name="deleteTask" />
)}
<Item name="separator" />
<Item name="zoomIn" />
<Item name="zoomOut" />
<Item name="separator" />
<Item
location="after"
widget="dxSelectBox"
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),
}}
/>
<Item location="after" widget="dxButton" options={getRefreshButtonOptions()} />
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
<Item location="after" widget="dxButton" options={getSettingButtonOptions()} />
)}
</Toolbar>
<Editing
enabled={gridDto.gridOptions.ganttOptionDto?.allowEditing}
allowTaskAdding={gridDto.gridOptions.ganttOptionDto?.allowTaskAdding}
allowTaskUpdating={gridDto.gridOptions.ganttOptionDto?.allowTaskUpdating}
allowTaskDeleting={gridDto.gridOptions.ganttOptionDto?.allowTaskDeleting}
allowDependencyAdding={gridDto.gridOptions.ganttOptionDto?.allowDependencyAdding}
allowDependencyDeleting={
gridDto.gridOptions.ganttOptionDto?.allowDependencyDeleting
}
allowResourceAdding={gridDto.gridOptions.ganttOptionDto?.allowResourceAdding}
allowResourceDeleting={gridDto.gridOptions.ganttOptionDto?.allowResourceDeleting}
/>
<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>
</div>
</>
)}
</Container>
</>
)
}
export default GanttView