Gantt düzenlemesi

This commit is contained in:
Sedat Öztürk 2025-12-02 00:27:20 +03:00
parent 3e588fb98b
commit 899e19b800
17 changed files with 748 additions and 335 deletions

View file

@ -38,5 +38,25 @@ public class TreeOptionDto
/// Alt kayıtlar seçildiğinde parent kayıtları da seç (recursive selection)
/// </summary>
public bool RecursiveSelection { get; set; } = false;
/// <summary>
/// Başlık alanı (örn: "Title")
/// </summary>
public string TitleExpr { get; set; }
/// <summary>
/// Başlangıç Tarihi ifadesi (örn: "0001-01-01")
/// </summary>
public string StartExpr { get; set; }
/// <summary>
/// Bitiş Tarihi ifadesi (örn: "9999-12-31")
/// </summary>
public string EndExpr { get; set; }
/// <summary>
/// İlerleme ifadesi (örn: "%50")
/// </summary>
public string ProgressExpr { get; set; }
}

View file

@ -4561,6 +4561,30 @@
"en": "Parent Field Name",
"tr": "Üst Kimlik İfadesi"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.TitleExpr",
"en": "Title Field Name",
"tr": "Başlık Alanı Adı"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.StartExpr",
"en": "Start Field Name",
"tr": "Başlangıç Alanı Adı"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.EndExpr",
"en": "End Field Name",
"tr": "Bitiş Alanı Adı"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ProgressExpr",
"en": "Progress Field Name",
"tr": "İlerleme Alanı Adı"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.RootValue",

View file

@ -53,7 +53,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -176,7 +177,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -299,7 +301,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -422,7 +425,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -545,7 +549,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -668,7 +673,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -791,7 +797,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -1199,7 +1206,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = true,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -1286,7 +1294,7 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.Tree,
ListFormType = ListFormTypeEnum.List,
IsSubForm = true,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -1422,7 +1430,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -1774,7 +1783,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
@ -2059,6 +2069,196 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
}
#endregion
#region Project Task Workload
listFormName = AppCodes.Project.Workload;
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
{
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,
CultureName = LanguageCodes.En,
ListFormCode = listFormName,
Name = listFormName,
Title = listFormName,
DataSourceCode = SeedConsts.DataSources.DefaultCode,
IsTenant = true,
IsBranch = false,
IsOrganizationUnit = false,
Description = listFormName,
SelectCommandType = SelectCommandTypeEnum.Table,
SelectCommand = TableNameResolver.GetFullViewName(nameof(TableNameEnum.ProjectTask)),
KeyFieldName = "Id",
KeyFieldDbSourceType = DbType.Guid,
DefaultFilter = DefaultFilterJson,
SortMode = GridOptions.SortModeSingle,
FilterRowJson = DefaultFilterRowJson,
HeaderFilterJson = DefaultHeaderFilterJson,
SearchPanelJson = DefaultSearchPanelJson,
GroupPanelJson = DefaultGroupPanelJson,
SelectionJson = DefaultSelectionSingleJson,
ColumnOptionJson = DefaultColumnOptionJson,
PermissionJson = DefaultPermissionJson(listFormName),
TreeOptionJson = JsonSerializer.Serialize(new TreeOptionDto
{
KeyExpr = "Id",
ParentIdExpr = "ParentId",
RootValue = null,
AutoExpandAll = true,
TitleExpr = "Name",
StartExpr = "StartDate",
EndExpr = "EndDate",
ProgressExpr = "Progress"
}),
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.ProjectTask)),
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson,
PagerOptionJson = DefaultPagerOptionJson,
EditingOptionJson = DefaultEditingOptionJson(listFormName, 800, 400, true, true, true, true, false),
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson,
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
{
new() {
Order=1, ColCount=2, ColSpan=1, ItemType="group", Items =[
new EditingFormItemDto { Order = 1, DataField="Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 2, DataField="Description", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea },
new EditingFormItemDto { Order = 3, DataField="StartDate", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox, EditorOptions = EditorOptionValues.DateTimeFormat },
new EditingFormItemDto { Order = 4, DataField="EndDate", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox, EditorOptions = EditorOptionValues.DateTimeFormat },
]}
}),
}, autoSave: true
);
#region Project Task Workload Fields
await _listFormFieldRepository.InsertManyAsync([
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "Id",
Width = 100,
ListOrderNo = 1,
Visible = false,
IsActive = true,
IsDeleted = false,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "ParentId",
Width = 200,
ListOrderNo = 2,
Visible = false,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
LookupJson = JsonSerializer.Serialize(new LookupDto
{
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.DefaultLookupQuery(nameof(TableNameEnum.Projects), "Id", "Name"),
CascadeEmptyFields = "PhaseId"
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Name",
Width = 200,
ListOrderNo = 3,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
SortIndex = 1,
SortDirection = GridColumnOptions.SortOrderAsc,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Description",
Width = 400,
ListOrderNo = 4,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.DateTime,
FieldName = "StartDate",
Width = 150,
ListOrderNo = 5,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.DateTime,
FieldName = "EndDate",
Width = 150,
ListOrderNo = 6,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Decimal,
FieldName = "Progress",
Width = 100,
ListOrderNo = 7,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
]);
#endregion
}
#endregion
#region Project Task Daily
listFormName = AppCodes.Project.ProjectTaskDaily;
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
@ -2066,7 +2266,8 @@ public class ListFormSeeder_Project : IDataSeedContributor, ITransientDependency
var listForm = await _listFormRepository.InsertAsync(
new ListForm()
{
ListFormType = ListFormTypeEnum.List, ExportJson = DefaultExportJson,
ListFormType = ListFormTypeEnum.List,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson,

View file

@ -2558,7 +2558,6 @@
"RequiredPermissionName": null,
"IsDisabled": false
},
{
"ParentCode": "App.Store.Definitions",
"Code": "App.Store.WarehouseType",
@ -2804,7 +2803,7 @@
"Code": "App.Project.Workload",
"DisplayName": "App.Project.Workload",
"Order": 7,
"Url": "/admin/projects/workload",
"Url": "/admin/list/App.Project.Workload",
"Icon": "FcComboChart",
"RequiredPermissionName": "App.Project.Workload",
"IsDisabled": false

View file

@ -5557,7 +5557,6 @@
"MultiTenancySide": 3,
"MenuGroup": "Erp"
},
{
"GroupName": "App.SupplyChain",
"Name": "App.SupplyChain.SupplyType",
@ -5999,7 +5998,6 @@
"MultiTenancySide": 3,
"MenuGroup": "Erp"
},
{
"GroupName": "App.SupplyChain",
"Name": "App.SupplyChain.QuotationStatus",
@ -6423,7 +6421,6 @@
"MultiTenancySide": 3,
"MenuGroup": "Erp"
},
{
"GroupName": "App.SupplyChain",
"Name": "App.SupplyChain.QuotationItem",
@ -6487,7 +6484,6 @@
"MultiTenancySide": 3,
"MenuGroup": "Erp"
},
{
"GroupName": "App.SupplyChain",
"Name": "App.SupplyChain.Approval",
@ -13041,4 +13037,4 @@
"MenuGroup": "Erp"
}
]
}
}

View file

@ -92,11 +92,11 @@ public static class SeederDefaults
Mode = GridOptions.SelectionModeMultiple,
SelectAllMode = GridOptions.SelectionAllModeAllPages
});
public static string DefaultTreeOptionJson(string KeyExpr, string ParentIdExpr, bool AutoExpandAll = true) => JsonSerializer.Serialize(new TreeOptionDto
public static string DefaultTreeOptionJson(string KeyExpr, string ParentIdExpr, bool AutoExpandAll = true, object RootValue = null) => JsonSerializer.Serialize(new TreeOptionDto
{
KeyExpr = KeyExpr,
ParentIdExpr = ParentIdExpr,
RootValue = null,
RootValue = RootValue,
AutoExpandAll = AutoExpandAll
});
public static readonly string DefaultPagerOptionJson = JsonSerializer.Serialize(new GridPagerOptionDto

View file

@ -5,8 +5,5 @@ public static class ListFormTypeEnum
public const string List = "List";
public const string Form = "Form";
public const string Chart = "Chart";
public const string Pivot = "Pivot";
public const string Tree = "Tree";
public const string Gantt = "Gantt";
}

View file

@ -587,6 +587,7 @@ public static class SeedConsts
public const string ProjectRisk = Default + ".ProjectRisk";
public const string ProjectTeam = Default + ".ProjectTeam";
public const string ProjectTask = Default + ".ProjectTask";
public const string Workload = Default + ".Workload";
public const string ProjectTaskDaily = Default + ".ProjectTaskDaily";
}

View file

@ -36,6 +36,8 @@ public class DatabaseViewSeeder : IDataSeedContributor, ITransientDependency
await CreateOrUpdateMaterialGroupView(dbContext, nameof(TableNameEnum.MaterialGroup));
await CreateOrUpdateProjectTaskView(dbContext, nameof(TableNameEnum.ProjectTask));
_logger.LogInformation("Database view seeding completed successfully.");
}
catch (Exception ex)
@ -102,5 +104,75 @@ public class DatabaseViewSeeder : IDataSeedContributor, ITransientDependency
throw;
}
}
/// <summary>
/// Creates or updates the V_T_Scp_ProjectWorkload view
/// </summary>
private async Task CreateOrUpdateProjectTaskView(PlatformDbContext dbContext, string viewName)
{
string fullViewName = TableNameResolver.GetFullViewName(viewName);
try
{
var createViewSql = $@"
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[{fullViewName}]'))
DROP VIEW [dbo].[{fullViewName}];
EXEC('
CREATE VIEW [dbo].[{fullViewName}]
AS
SELECT
TenantId,
Id,
NULL AS ParentId,
[Name],
[Description],
[StartDate],
[EndDate],
[IsDeleted],
[Progress]
FROM [Erp].[dbo].[Prj_T_Projects]
UNION ALL
SELECT
TenantId,
Id,
ProjectId AS ParentId,
[Name],
[Description],
[StartDate],
[EndDate],
[IsDeleted],
100 AS [Progress]
FROM [Erp].[dbo].[Prj_T_ProjectPhase]
UNION ALL
SELECT
TenantId,
Id,
PhaseId AS ParentId,
[Name],
[Description],
[StartDate],
[EndDate],
[IsDeleted],
[Progress]
FROM [Erp].[dbo].[Prj_T_ProjectTask]
')
";
await dbContext.Database.ExecuteSqlRawAsync(createViewSql);
_logger.LogInformation($"Created/Updated {fullViewName} view successfully.");
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred while creating {fullViewName} view.");
throw;
}
}
}

View file

@ -1,3 +1,5 @@
import { Gantt } from 'devextreme-react'
export const extraFilterControlTypeOptions = [
{ value: 'Select', label: 'Select' },
{ value: 'TextBox', label: 'TextBox' },
@ -73,55 +75,55 @@ export const ListFormEditTabs = {
} as const
export const tabVisibilityConfig: Record<string, string[]> = {
List: [
'details',
'database',
'permissions',
'commands',
'edit',
'sortingForm',
'filterRow',
'rowForm',
'search',
'group',
'select',
'columns',
'pivots',
'tree',
'pager',
'state',
'extrafilter',
'customization',
'fields',
'editForm',
'widget',
//Chart tabları
'commonSettings',
'series',
'axis',
'panes',
'animationsOptions',
'annotations',
'zoomAndPanSettings',
'legendSettings',
'exportSettings',
'crosshairOptions',
'subForms',
],
Form: [
'details',
'database',
'permissions',
'commands',
'edit',
'fields',
'editForm',
'widget',
'subForms',
],
}
List: [
'details',
'database',
'permissions',
'commands',
'edit',
'sortingForm',
'filterRow',
'rowForm',
'search',
'group',
'select',
'columns',
'pivots',
'tree',
'pager',
'state',
'extrafilter',
'customization',
'fields',
'editForm',
'widget',
//Chart tabları
'commonSettings',
'series',
'axis',
'panes',
'animationsOptions',
'annotations',
'zoomAndPanSettings',
'legendSettings',
'exportSettings',
'crosshairOptions',
'subForms',
],
Form: [
'details',
'database',
'permissions',
'commands',
'edit',
'fields',
'editForm',
'widget',
'subForms',
],
}
export const themeOptions = [
export const themeOptions = [
{ value: 'generic.dark', label: 'Generic Dark' },
{ value: 'generic.light', label: 'Generic Carmine' },
{ value: 'generic.contrast', label: 'Generic Contrast' },

View file

@ -386,6 +386,10 @@ export interface TreeOptionDto {
expandedRowKeys?: any[]
autoExpandAll?: boolean
recursiveSelection?: boolean
titleExpr?: string
startExpr?: string
endExpr?: string
progressExpr?: string
}
export interface GridEditingDto {

View file

@ -36,7 +36,7 @@ import FormTabWidgets from './FormTabWidgets'
import FormTabExtraFilters from './FormTabExtraFilters'
import FormTabEditForm from './FormTabEditForm'
import FormTabPivots from './FormTabPivots'
import FormTabTree from './FormTabTree'
import FormTabTreeGantt from './FormTabTreeGantt'
import ChartTabAnimation from './ChartTabAnimation'
import ChartTabAnnotations from './ChartTabAnnotations'
import ChartTabZoomAndPan from './ChartTabZoomAndPan'
@ -360,7 +360,7 @@ const FormEdit = () => {
<FormTabPivots onSubmit={onSubmit} />
</TabContent>
<TabContent value="tree" className="px-2">
<FormTabTree onSubmit={onSubmit} />
<FormTabTreeGantt onSubmit={onSubmit} />
</TabContent>
<TabContent value="pager" className="px-2">
<FormTabPager onSubmit={onSubmit} />

View file

@ -28,6 +28,7 @@ const schema = Yup.object().shape({
pivot: Yup.boolean(),
chart: Yup.boolean(),
tree: Yup.boolean(),
gantt: Yup.boolean(),
defaultLayout: Yup.string(),
cardLayoutColumn: Yup.number(),
}),

View file

@ -1,203 +0,0 @@
import { Container } from '@/components/shared'
import {
Button,
Notification,
Checkbox,
FormContainer,
FormItem,
Input,
Select,
toast,
} from '@/components/ui'
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
import { useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Field, FieldProps, Form, Formik } from 'formik'
import * as Yup from 'yup'
import { FormEditProps } from './FormEdit'
import { SelectBoxOption } from '@/types/shared'
import { useEffect, useState } from 'react'
import { getListFormFields } from '@/services/admin/list-form-field.service'
import { groupBy } from 'lodash'
import { useParams } from 'react-router-dom'
const validationSchema = Yup.object().shape({})
function FormTabTree(props: FormEditProps) {
const { listFormCode } = useParams()
const { translate } = useLocalization()
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
const getFields = async () => {
if (!listFormCode) {
return
}
try {
const resp = await getListFormFields({
listFormCode,
sorting: 'ListOrderNo',
maxResultCount: 1000,
})
if (resp.data?.items) {
const fieldNames = groupBy(resp?.data?.items, 'fieldName')
setFieldList(
Object.keys(fieldNames).map((a) => ({
value: a,
label: a,
})),
)
}
} catch (error: any) {
toast.push(
<Notification type="danger" duration={2000}>
Alanlar getirilemedi
{error.toString()}
</Notification>,
{
placement: 'top-end',
},
)
}
}
useEffect(() => {
getFields()
}, [listFormCode])
const listFormValues = useStoreState((s) => s.admin.lists.values)
if (!listFormValues) {
return null
}
return (
<Container className="grid xl:grid-cols-2">
<Formik
initialValues={listFormValues}
validationSchema={validationSchema}
onSubmit={async (values, formikHelpers) => {
await props.onSubmit(ListFormEditTabs.TreeForm, values, formikHelpers)
}}
>
{({ touched, errors, isSubmitting, values }) => (
<Form>
<FormContainer size="sm">
<FormItem
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
invalid={errors.treeOptionDto?.keyExpr && touched.treeOptionDto?.keyExpr}
errorMessage={errors.treeOptionDto?.keyExpr}
>
<Field
type="text"
name="treeOptionDto.keyExpr"
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.keyExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ParentIdExpr')}
invalid={errors.treeOptionDto?.parentIdExpr && touched.treeOptionDto?.parentIdExpr}
errorMessage={errors.treeOptionDto?.parentIdExpr}
>
<Field
type="text"
name="treeOptionDto.parentIdExpr"
placeholder={translate('::ListForms.ListFormEdit.ParentIdExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.parentIdExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.HasItemsExpr')}
invalid={errors.treeOptionDto?.hasItemsExpr && touched.treeOptionDto?.hasItemsExpr}
errorMessage={errors.treeOptionDto?.hasItemsExpr}
>
<Field
type="text"
autoComplete="off"
name="treeOptionDto.hasItemsExpr"
placeholder={translate('::ListForms.ListFormEdit.HasItemsExpr')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.RootValue')}
invalid={!!(errors.treeOptionDto?.rootValue && touched.treeOptionDto?.rootValue)}
errorMessage={errors.treeOptionDto?.rootValue as string}
>
<Field
type="text"
autoComplete="off"
name="treeOptionDto.rootValue"
placeholder={translate('::ListForms.ListFormEdit.RootValue')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.AutoExpandAll')}
invalid={
errors.treeOptionDto?.autoExpandAll && touched.treeOptionDto?.autoExpandAll
}
errorMessage={errors.treeOptionDto?.autoExpandAll}
>
<Field
name="treeOptionDto.autoExpandAll"
placeholder={translate('::ListForms.ListFormEdit.AutoExpandAll')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.RecursiveSelection')}
invalid={
errors.treeOptionDto?.recursiveSelection &&
touched.treeOptionDto?.recursiveSelection
}
errorMessage={errors.treeOptionDto?.recursiveSelection}
>
<Field
name="treeOptionDto.recursiveSelection"
placeholder={translate('::ListForms.ListFormEdit.RecursiveSelection')}
component={Checkbox}
/>
</FormItem>
<Button block variant="solid" loading={isSubmitting} type="submit" className="my-2">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</FormContainer>
</Form>
)}
</Formik>
</Container>
)
}
export default FormTabTree

View file

@ -0,0 +1,315 @@
import { Container } from '@/components/shared'
import {
Button,
Notification,
Checkbox,
FormContainer,
FormItem,
Input,
Select,
toast,
Card,
} from '@/components/ui'
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
import { useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Field, FieldProps, Form, Formik } from 'formik'
import * as Yup from 'yup'
import { FormEditProps } from './FormEdit'
import { SelectBoxOption } from '@/types/shared'
import { useEffect, useState } from 'react'
import { getListFormFields } from '@/services/admin/list-form-field.service'
import { groupBy } from 'lodash'
import { useParams } from 'react-router-dom'
const validationSchema = Yup.object().shape({})
function FormTabTreeGantt(props: FormEditProps) {
const { listFormCode } = useParams()
const { translate } = useLocalization()
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
const getFields = async () => {
if (!listFormCode) {
return
}
try {
const resp = await getListFormFields({
listFormCode,
sorting: 'ListOrderNo',
maxResultCount: 1000,
})
if (resp.data?.items) {
const fieldNames = groupBy(resp?.data?.items, 'fieldName')
setFieldList(
Object.keys(fieldNames).map((a) => ({
value: a,
label: a,
})),
)
}
} catch (error: any) {
toast.push(
<Notification type="danger" duration={2000}>
Alanlar getirilemedi
{error.toString()}
</Notification>,
{
placement: 'top-end',
},
)
}
}
useEffect(() => {
getFields()
}, [listFormCode])
const listFormValues = useStoreState((s) => s.admin.lists.values)
if (!listFormValues) {
return null
}
return (
<Formik
initialValues={listFormValues}
validationSchema={validationSchema}
onSubmit={async (values, formikHelpers) => {
await props.onSubmit(ListFormEditTabs.TreeForm, values, formikHelpers)
}}
>
{({ touched, errors, isSubmitting, values }) => (
<Form>
<FormContainer size="sm">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<Card className="my-2" header="Tree Options">
<FormItem
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
invalid={errors.treeOptionDto?.keyExpr && touched.treeOptionDto?.keyExpr}
errorMessage={errors.treeOptionDto?.keyExpr}
>
<Field
type="text"
name="treeOptionDto.keyExpr"
placeholder={translate(
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName',
)}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.keyExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ParentIdExpr')}
invalid={
errors.treeOptionDto?.parentIdExpr && touched.treeOptionDto?.parentIdExpr
}
errorMessage={errors.treeOptionDto?.parentIdExpr}
>
<Field
type="text"
name="treeOptionDto.parentIdExpr"
placeholder={translate('::ListForms.ListFormEdit.ParentIdExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.parentIdExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.HasItemsExpr')}
invalid={
errors.treeOptionDto?.hasItemsExpr && touched.treeOptionDto?.hasItemsExpr
}
errorMessage={errors.treeOptionDto?.hasItemsExpr}
>
<Field
type="text"
autoComplete="off"
name="treeOptionDto.hasItemsExpr"
placeholder={translate('::ListForms.ListFormEdit.HasItemsExpr')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.RootValue')}
invalid={!!(errors.treeOptionDto?.rootValue && touched.treeOptionDto?.rootValue)}
errorMessage={errors.treeOptionDto?.rootValue as string}
>
<Field
type="text"
autoComplete="off"
name="treeOptionDto.rootValue"
placeholder={translate('::ListForms.ListFormEdit.RootValue')}
component={Input}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.AutoExpandAll')}
invalid={
errors.treeOptionDto?.autoExpandAll && touched.treeOptionDto?.autoExpandAll
}
errorMessage={errors.treeOptionDto?.autoExpandAll}
>
<Field
name="treeOptionDto.autoExpandAll"
placeholder={translate('::ListForms.ListFormEdit.AutoExpandAll')}
component={Checkbox}
/>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.RecursiveSelection')}
invalid={
errors.treeOptionDto?.recursiveSelection &&
touched.treeOptionDto?.recursiveSelection
}
errorMessage={errors.treeOptionDto?.recursiveSelection}
>
<Field
name="treeOptionDto.recursiveSelection"
placeholder={translate('::ListForms.ListFormEdit.RecursiveSelection')}
component={Checkbox}
/>
</FormItem>
</Card>
<Card className="my-2" header="Gantt Options">
<FormItem
label={translate('::ListForms.ListFormEdit.TitleExpr')}
invalid={errors.treeOptionDto?.titleExpr && touched.treeOptionDto?.titleExpr}
errorMessage={errors.treeOptionDto?.titleExpr}
>
<Field
type="text"
name="treeOptionDto.titleExpr"
placeholder={translate('::ListForms.ListFormEdit.TitleExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.titleExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.StartExpr')}
invalid={errors.treeOptionDto?.startExpr && touched.treeOptionDto?.startExpr}
errorMessage={errors.treeOptionDto?.startExpr}
>
<Field
type="text"
name="treeOptionDto.startExpr"
placeholder={translate('::ListForms.ListFormEdit.StartExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.startExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.EndExpr')}
invalid={errors.treeOptionDto?.endExpr && touched.treeOptionDto?.endExpr}
errorMessage={errors.treeOptionDto?.endExpr}
>
<Field
type="text"
name="treeOptionDto.endExpr"
placeholder={translate('::ListForms.ListFormEdit.EndExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.endExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ProgressExpr')}
invalid={
errors.treeOptionDto?.progressExpr && touched.treeOptionDto?.progressExpr
}
errorMessage={errors.treeOptionDto?.progressExpr}
>
<Field
type="text"
name="treeOptionDto.progressExpr"
placeholder={translate('::ListForms.ListFormEdit.ProgressExpr')}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList?.filter(
(option) => option.value === values.treeOptionDto.progressExpr,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
</Card>
</div>
<Button block variant="solid" loading={isSubmitting} type="submit" className="my-2">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</FormContainer>
</Form>
)}
</Formik>
)
}
export default FormTabTreeGantt

View file

@ -203,7 +203,6 @@ export const columnEditorTypeListOptions = [
export const listFormTypeOptions = [
{ value: 'Form', label: 'Form' },
{ value: 'List', label: 'List' },
{ value: 'Tree', label: 'Tree' },
{ value: 'Chart', label: 'Chart' },
]

View file

@ -6,17 +6,15 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
import useResponsive from '@/utils/hooks/useResponsive'
import Gantt, {
Column,
Dependencies,
Editing,
GanttRef,
GanttTypes,
ResourceAssignments,
Resources,
Item,
Tasks,
Toolbar,
Validation,
} from 'devextreme-react/gantt'
import { Button } from '@/components/ui'
import CustomStore from 'devextreme/data/custom_store'
import DataSource from 'devextreme/data/data_source'
import { useCallback, useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet'
import { getList } from '@/services/form.service'
@ -45,7 +43,7 @@ const GanttView = (props: GanttViewProps) => {
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const [ganttDataSource, setGanttDataSource] = useState<CustomStore<any, any>>()
const [ganttDataSource, setGanttDataSource] = useState<DataSource<any, any>>()
const [gridDto, setGridDto] = useState<GridDto>()
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
@ -132,7 +130,7 @@ const GanttView = (props: GanttViewProps) => {
useEffect(() => {
if (!gridDto) return
const dataSource = createSelectDataSource(
const customStore = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
@ -140,8 +138,14 @@ const GanttView = (props: GanttViewProps) => {
undefined,
)
// Gantt için DataSource wrapper'ı oluştur
const dataSource = new DataSource({
store: customStore,
reshapeOnPush: true,
})
setGanttDataSource(dataSource)
}, [gridDto, searchParams])
}, [gridDto, searchParams, createSelectDataSource])
useEffect(() => {
refListFormCode.current = listFormCode
@ -207,46 +211,6 @@ const GanttView = (props: GanttViewProps) => {
{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}
@ -254,6 +218,12 @@ const GanttView = (props: GanttViewProps) => {
id={'Gantt-' + listFormCode}
taskListWidth={500}
scaleType="weeks"
rootValue={
gridDto.gridOptions.treeOptionDto?.rootValue === '' ||
gridDto.gridOptions.treeOptionDto?.rootValue === undefined
? null
: gridDto.gridOptions.treeOptionDto?.rootValue
}
height={
gridDto.gridOptions.height > 0
? gridDto.gridOptions.height
@ -267,17 +237,32 @@ const GanttView = (props: GanttViewProps) => {
onTaskUpdated={onTaskUpdated}
onTaskDeleted={onTaskDeleted}
>
<Tasks dataSource={ganttDataSource} />
<Dependencies dataSource={[]} />
<Resources dataSource={[]} />
<ResourceAssignments dataSource={[]} />
<Tasks
dataSource={ganttDataSource}
keyExpr={gridDto.gridOptions.treeOptionDto?.keyExpr}
parentIdExpr={gridDto.gridOptions.treeOptionDto?.parentIdExpr}
titleExpr={gridDto.gridOptions.treeOptionDto?.titleExpr}
startExpr={gridDto.gridOptions.treeOptionDto?.startExpr}
endExpr={gridDto.gridOptions.treeOptionDto?.endExpr}
progressExpr={gridDto.gridOptions.treeOptionDto?.progressExpr}
/>
<Toolbar>
<Item name="undo" />
<Item name="redo" />
<Item name="separator" />
<Item name="collapseAll" />
<Item name="expandAll" />
<Item name="separator" />
<Item name="addTask" />
<Item name="deleteTask" />
<Item name="separator" />
<Item name="zoomIn" />
<Item name="zoomOut" />
</Toolbar>
<Editing
enabled={
gridDto.gridOptions.editingOptionDto?.allowAdding ||
gridDto.gridOptions.editingOptionDto?.allowUpdating ||
gridDto.gridOptions.editingOptionDto?.allowDeleting
}
enabled={gridDto.gridOptions.editingOptionDto?.allowUpdating}
allowTaskAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
allowTaskUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
allowTaskDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}