From 3e588fb98b2119ec20eba17d9f68d06ab1f43a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:45:23 +0300 Subject: [PATCH] Gantt Layout --- .../ListForms/GridOptionsDto/LayoutDto.cs | 3 +- .../Seeds/LanguagesData.json | 10 +- .../Seeds/ListFormSeeder_Hr.cs | 10 +- .../Seeds/SeederDefaults.cs | 2 + .../Enums/ListFormTypeEnum.cs | 1 + ui/src/proxy/form/models.ts | 1 + .../admin/listForm/edit/FormTabDetails.tsx | 18 +- ui/src/views/admin/listForm/edit/types.ts | 3 +- ui/src/views/list/GanttView.tsx | 317 ++++++++++++++++++ ui/src/views/list/List.tsx | 24 ++ 10 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 ui/src/views/list/GanttView.tsx diff --git a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs index eaa6dd77..d2524075 100644 --- a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs +++ b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs @@ -5,8 +5,9 @@ public class LayoutDto public bool Grid { get; set; } = true; public bool Card { get; set; } = true; public bool Pivot { get; set; } = true; - public bool Tree { get; set; } = true; public bool Chart { get; set; } = true; + public bool Tree { get; set; } = true; + public bool Gantt { get; set; } = true; public string DefaultLayout { get; set; } = "grid"; public int CardLayoutColumn { get; set; } = 4; } diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json index 80e187d2..629951dc 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json @@ -3592,8 +3592,8 @@ { "resourceName": "Platform", "key": "ListForms.ListFormEdit.TabTree", - "en": "Tree", - "tr": "Ağaç" + "en": "Tree & Gantt", + "tr": "Ağaç & Gantt" }, { "resourceName": "Platform", @@ -3799,6 +3799,12 @@ "en": "Full Height", "tr": "Tam Yükseklik" }, + { + "resourceName": "Platform", + "key": "ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout", + "en": "Gantt Layout", + "tr": "Gantt Düzeni" + }, { "resourceName": "Platform", "key": "ListForms.ListFormEdit.DetailsLayoutDto.GridLayout", diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs index 4ac42e5d..280c3f44 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs @@ -1496,11 +1496,11 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency DisplayExpr = "name", ValueExpr = "key", LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { - new () { Key= "Active", Name= "Active"}, - new () { Key= "Inactive", Name= "Inactive" }, - new () { Key= "OnLeave", Name= "OnLeave" }, - new () { Key= "Suspended", Name= "Suspended" }, - new () { Key= "Terminated", Name= "Terminated" }, + new () { Key= "Aktif", Name= "Aktif"}, + new () { Key= "Pasif", Name= "Pasif" }, + new () { Key= "İzinli", Name= "İzinli" }, + new () { Key= "Askıda", Name= "Askıda" }, + new () { Key= "Sonlandırıldı", Name= "Sonlandırıldı" }, }), }), PermissionJson = DefaultFieldPermissionJson(listForm.Name), diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs index 2c24e886..849934dc 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs @@ -68,6 +68,8 @@ public static class SeederDefaults Card = true, Pivot = true, Chart = true, + Tree = true, + Gantt = true, DefaultLayout = "grid", CardLayoutColumn = 4 }); diff --git a/api/src/Erp.Platform.Domain.Shared/Enums/ListFormTypeEnum.cs b/api/src/Erp.Platform.Domain.Shared/Enums/ListFormTypeEnum.cs index 1bb9e9cf..bcb1df26 100644 --- a/api/src/Erp.Platform.Domain.Shared/Enums/ListFormTypeEnum.cs +++ b/api/src/Erp.Platform.Domain.Shared/Enums/ListFormTypeEnum.cs @@ -7,5 +7,6 @@ public static class ListFormTypeEnum public const string Chart = "Chart"; public const string Pivot = "Pivot"; public const string Tree = "Tree"; + public const string Gantt = "Gantt"; } diff --git a/ui/src/proxy/form/models.ts b/ui/src/proxy/form/models.ts index e3a98b27..973bbeb8 100644 --- a/ui/src/proxy/form/models.ts +++ b/ui/src/proxy/form/models.ts @@ -820,6 +820,7 @@ export interface LayoutDto { pivot: boolean tree: boolean chart: boolean + gantt: boolean defaultLayout: ListViewLayoutType cardLayoutColumn: number } diff --git a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx index 769d7109..1f06a3b5 100644 --- a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx @@ -107,7 +107,7 @@ function FormTabDetails( name="listFormType" placeholder={translate('::ListForms.ListFormEdit.ListFormType')} > - {({ field, form }: FieldProps) => ( + {({ field, form }: FieldProps) => ( + + + + Promise + gridDto?: GridDto +} + +const GanttView = (props: GanttViewProps) => { + const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props + const { translate } = useLocalization() + const { smaller } = useResponsive() + + const ganttRef = useRef() + const refListFormCode = useRef('') + const widgetGroupRef = useRef(null) + + const [ganttDataSource, setGanttDataSource] = useState>() + const [gridDto, setGridDto] = useState() + 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 ( + <> +
+ +
+ + + {!isSubForm && ( + + )} + {!gridDto &&
Loading gantt configuration...
} + {gridDto && !ganttDataSource &&
Loading data source...
} + {gridDto && ganttDataSource && ( + <> + {/* Custom Toolbar */} + {(toolbarData.length > 0 || filterToolbarData.length > 0) && ( +
+ {toolbarData.map((item: any) => { + if (item.widget === 'dxButton' && item.options) { + return ( + + ) + } + return null + })} + {filterToolbarData.map((item: any) => { + if (item.widget === 'dxButton' && item.options) { + return ( + + ) + } + return null + })} +
+ )} + +
+ 0 + ? gridDto.gridOptions.height + : gridDto.gridOptions.fullHeight + ? `calc(100vh - ${170 + widgetGroupHeight}px)` + : 700 + } + showResources={true} + showDependencies={true} + onTaskInserted={onTaskInserted} + onTaskUpdated={onTaskUpdated} + onTaskDeleted={onTaskDeleted} + > + + + + + + + + + + {ganttColumns.map((col: any) => ( + + ))} + +
+ + )} + + setToolbarModalData(undefined)} + onRequestClose={() => setToolbarModalData(undefined)} + > + {toolbarModalData?.content} + +
+ + ) +} + +export default GanttView diff --git a/ui/src/views/list/List.tsx b/ui/src/views/list/List.tsx index 27aef9d0..6fb9612a 100644 --- a/ui/src/views/list/List.tsx +++ b/ui/src/views/list/List.tsx @@ -15,6 +15,8 @@ import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { ListViewLayoutType } from '../admin/listForm/edit/types' import Chart from './Chart' import Card from './Card' +import { FaChartGantt } from 'react-icons/fa6' +import GanttView from './GanttView' const List = () => { const params = useParams() @@ -93,6 +95,21 @@ const List = () => { )}
+ {gridDto?.gridOptions?.layoutDto.gantt && + gridDto?.gridOptions?.treeOptionDto?.parentIdExpr && ( + + )} + {gridDto?.gridOptions?.layoutDto.tree && gridDto?.gridOptions?.treeOptionDto?.parentIdExpr && (