Gantt, Scheduler kolonları ve Tree keyfieldName çağrısı

This commit is contained in:
Sedat ÖZTÜRK 2026-07-28 17:00:38 +03:00
parent ab6a33c340
commit 3e50a28176
6 changed files with 483 additions and 38 deletions

View file

@ -0,0 +1,17 @@
IF OBJECT_ID(N'[dbo].[Pro_D_Gantt]', 'U') IS NULL
BEGIN
CREATE TABLE [dbo].[Pro_D_Gantt]
(
[Id] uniqueidentifier NOT NULL DEFAULT NEWID(),
[ParentId] uniqueidentifier NULL,
[Title] nvarchar(300) NOT NULL,
[StartDate] datetime2 NOT NULL,
[EndDate] datetime2 NOT NULL,
[Progress] int NOT NULL DEFAULT 0,
CONSTRAINT [PK_Pro_D_Gantt] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO

View file

@ -2,13 +2,14 @@ IF OBJECT_ID(N'[dbo].[Pro_T_Approval]', 'U') IS NULL
BEGIN
CREATE TABLE [dbo].[Pro_T_Approval]
(
[Id] uniqueidentifier NOT NULL DEFAULT NEWID(),
[ApprovalUserName] nvarchar(256) NULL,
[ApprovalStatus] nvarchar(50) NULL,
[Id] uniqueidentifier NOT NULL,
[ApprovalUserName] nvarchar(512) NULL,
[ApprovalStatus] nvarchar(100) NULL,
[ApprovalDate] datetime NULL,
[ApprovalDescription] nvarchar(200) NULL,
[ApprovalDescription] nvarchar(400) NULL,
[TenantId] uniqueidentifier NULL,
[Title] nvarchar(100) NULL,
[Title] nvarchar(200) NULL,
[ParentTitle] nvarchar(200) NULL,
CONSTRAINT [PK_Pro_T_Approval] PRIMARY KEY NONCLUSTERED
(
[Id] ASC

View file

@ -11,7 +11,7 @@
"AllowDeleting": true,
"AllowDetail": false,
"ConfirmDelete": true,
"DefaultLayout": "grid",
"DefaultLayout": "tree",
"Grid": true,
"Card": true,
"Pivot": true,
@ -39,9 +39,9 @@
"SelectCommand": "Pro_T_Approval",
"KeyFieldName": "Id",
"KeyFieldDbSourceType": 9,
"TreeKeyExpr": "",
"TreeParentIdExpr": "",
"TreeAutoExpandAll": false,
"TreeKeyExpr": "Title",
"TreeParentIdExpr": "ParentTitle",
"TreeAutoExpandAll": true,
"GanttKeyExpr": "",
"GanttParentIdExpr": "",
"GanttAutoExpandAll": false,
@ -248,6 +248,23 @@
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"FieldName": "ParentTitle",
"CaptionName": "App.Listform.ListformField.ParentTitle",
"EditorType": "dxSelectBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": true,
"DbSourceType": 16,
"TurkishCaption": "Parent Title",
"EnglishCaption": "Parent Title",
"LookupDataSourceType": 2,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": "SELECT \u0022Title\u0022 AS \u0022Key\u0022, \u0022Title\u0022 AS \u0022Name\u0022 FROM \u0022Pro_T_Approval\u0022 ORDER BY \u0022Title\u0022;"
}
]
}
@ -261,7 +278,8 @@
"LanguageKeys": [
"App.Wizard.Approval",
"App.Wizard.Approval.Title",
"App.Wizard.Approval.Desc"
"App.Wizard.Approval.Desc",
"App.Listform.ListformField.ParentTitle"
],
"PermissionGroupNames": [],
"PermissionNames": [

View file

@ -0,0 +1,208 @@
{
"Wizard": {
"WizardName": "Gantt",
"ListFormCode": "App.Wizard.Gantt",
"MenuCode": "App.Wizard.Gantt",
"IsTenant": false,
"IsBranch": false,
"IsOrganizationUnit": false,
"AllowAdding": true,
"AllowUpdating": true,
"AllowDeleting": true,
"AllowDetail": false,
"ConfirmDelete": true,
"DefaultLayout": "gantt",
"Grid": true,
"Card": true,
"Pivot": true,
"Tree": true,
"Chart": true,
"Gantt": true,
"Scheduler": true,
"Todo": false,
"LanguageTextMenuEn": "Gantt",
"LanguageTextMenuTr": "Gantt",
"LanguageTextTitleEn": "Gantt",
"LanguageTextTitleTr": "Gantt",
"LanguageTextDescEn": "Gantt",
"LanguageTextDescTr": "Gantt",
"LanguageTextMenuParentEn": "Project",
"LanguageTextMenuParentTr": "Project",
"PermissionGroupName": "App.Wizard.Project",
"MenuParentCode": "App.Wizard.Project",
"MenuParentShortName": "Pro",
"MenuParentIcon": "FcVideoProjector",
"MenuIcon": "FcBullish",
"DataSourceCode": "Default",
"DataSourceConnectionString": "",
"SelectCommandType": 1,
"SelectCommand": "Pro_D_Gantt",
"KeyFieldName": "Id",
"KeyFieldDbSourceType": 9,
"TreeKeyExpr": "",
"TreeParentIdExpr": "",
"TreeAutoExpandAll": false,
"GanttKeyExpr": "Id",
"GanttParentIdExpr": "ParentId",
"GanttAutoExpandAll": true,
"GanttTitleExpr": "Title",
"GanttStartExpr": "StartDate",
"GanttEndExpr": "EndDate",
"GanttProgressExpr": "Progress",
"SchedulerTextExpr": "",
"SchedulerStartDateExpr": "",
"SchedulerEndDateExpr": "",
"TodoTitleExpr": "",
"TodoStatusExpr": "",
"TodoDescriptionExpr": "",
"TodoDueDateExpr": "",
"TodoTagExpr": "",
"TodoAssigneeExpr": "",
"TodoPriorityExpr": "",
"TodoCompletedExpr": "",
"TodoOrderExpr": "",
"TodoStatusOrder": "Backlog, Ready to Start, In Progress, Code Review, Testing, Staging",
"TodoAllowDragging": true,
"ApprovalUserFieldName": "",
"ApprovalDateFieldName": "",
"ApprovalStatusFieldName": "",
"ApprovalDescriptionFieldName": "",
"ApprovalIsFilterUserName": false,
"Criteria": [],
"Groups": [
{
"Caption": "",
"ColCount": 1,
"Items": [
{
"FieldName": "Id",
"CaptionName": "App.Listform.ListformField.Id",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 9,
"TurkishCaption": "Id",
"EnglishCaption": "Id",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"FieldName": "ParentId",
"CaptionName": "App.Listform.ListformField.ParentId",
"EditorType": "dxSelectBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": true,
"DbSourceType": 9,
"TurkishCaption": "Parent Id",
"EnglishCaption": "Parent Id",
"LookupDataSourceType": 2,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": "SELECT \u0022Id\u0022 AS \u0022Key\u0022, \u0022Title\u0022 AS \u0022Name\u0022 FROM \u0022Pro_D_Gantt\u0022 ORDER BY \u0022Title\u0022;"
},
{
"FieldName": "Title",
"CaptionName": "App.Listform.ListformField.Title",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 16,
"TurkishCaption": "Title",
"EnglishCaption": "Title",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"FieldName": "StartDate",
"CaptionName": "App.Listform.ListformField.StartDate",
"EditorType": "dxDateBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 6,
"TurkishCaption": "Start Date",
"EnglishCaption": "Start Date",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"FieldName": "EndDate",
"CaptionName": "App.Listform.ListformField.EndDate",
"EditorType": "dxDateBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 6,
"TurkishCaption": "End Date",
"EnglishCaption": "End Date",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"FieldName": "Progress",
"CaptionName": "App.Listform.ListformField.Progress",
"EditorType": "dxNumberBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 11,
"TurkishCaption": "Progress",
"EnglishCaption": "Progress",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
}
]
}
],
"SubForms": [],
"Widgets": []
},
"IsDeletedField": false,
"IsCreatedField": false,
"InsertedRecords": {
"LanguageKeys": [
"App.Wizard.Gantt",
"App.Wizard.Gantt.Title",
"App.Wizard.Gantt.Desc"
],
"PermissionGroupNames": [],
"PermissionNames": [
"App.Wizard.Gantt",
"App.Wizard.Gantt.Create",
"App.Wizard.Gantt.Update",
"App.Wizard.Gantt.Delete",
"App.Wizard.Gantt.Export",
"App.Wizard.Gantt.Import",
"App.Wizard.Gantt.Note"
],
"MenuCodes": [
"App.Wizard.Gantt"
],
"DataSourceCodes": []
}
}

View file

@ -372,6 +372,129 @@ const TODO_COLUMNS: ColumnDefinition[] = [
},
]
const GANTT_COLUMNS: ColumnDefinition[] = [
{
id: '__GanttId',
columnName: 'Id',
dataType: 'uniqueidentifier',
maxLength: '',
isNullable: false,
defaultValue: 'NEWID()',
description: 'Gantt task primary key',
},
{
id: '__GanttParentId',
columnName: 'ParentId',
dataType: 'uniqueidentifier',
maxLength: '',
isNullable: true,
defaultValue: '',
description: 'Parent Gantt task ID',
},
{
id: '__GanttTitle',
columnName: 'Title',
dataType: 'nvarchar',
maxLength: '300',
isNullable: false,
defaultValue: '',
description: 'Gantt task title',
},
{
id: '__GanttStartDate',
columnName: 'StartDate',
dataType: 'datetime2',
maxLength: '',
isNullable: false,
defaultValue: '',
description: 'Gantt task start date',
},
{
id: '__GanttEndDate',
columnName: 'EndDate',
dataType: 'datetime2',
maxLength: '',
isNullable: false,
defaultValue: '',
description: 'Gantt task end date',
},
{
id: '__GanttProgress',
columnName: 'Progress',
dataType: 'int',
maxLength: '',
isNullable: false,
defaultValue: '0',
description: 'Gantt task completion percentage',
},
]
const SCHEDULER_COLUMNS: ColumnDefinition[] = [
{
id: '__SchedulerId',
columnName: 'Id',
dataType: 'uniqueidentifier',
maxLength: '',
isNullable: false,
defaultValue: 'NEWID()',
description: 'Scheduler appointment primary key',
},
{
id: '__SchedulerText',
columnName: 'Text',
dataType: 'nvarchar',
maxLength: '300',
isNullable: false,
defaultValue: '',
description: 'Scheduler appointment text',
},
{
id: '__SchedulerStartDate',
columnName: 'StartDate',
dataType: 'datetime2',
maxLength: '',
isNullable: false,
defaultValue: '',
description: 'Scheduler appointment start date',
},
{
id: '__SchedulerEndDate',
columnName: 'EndDate',
dataType: 'datetime2',
maxLength: '',
isNullable: false,
defaultValue: '',
description: 'Scheduler appointment end date',
},
{
id: '__SchedulerAllDay',
columnName: 'AllDay',
dataType: 'bit',
maxLength: '',
isNullable: false,
defaultValue: '0',
description: 'Scheduler all-day appointment flag',
},
{
id: '__SchedulerRecurrenceRule',
columnName: 'RecurrenceRule',
dataType: 'nvarchar',
maxLength: '500',
isNullable: true,
defaultValue: '',
description: 'Scheduler recurrence rule',
},
{
id: '__SchedulerRecurrenceException',
columnName: 'RecurrenceException',
dataType: 'nvarchar(MAX)',
maxLength: '',
isNullable: true,
defaultValue: '',
description: 'Scheduler recurrence exceptions',
},
]
// ─── T-SQL Generator ──────────────────────────────────────────────────────────
function colToSqlLine(col: ColumnDefinition, addComma = true): string {
@ -1402,6 +1525,34 @@ const SqlTableDesignerDialog = ({
addAutomaticPrimaryKey('todo', 'Todo primary key (auto)')
}
const addGanttColumns = () => {
const existingNames = new Set(columns.map((c) => c.columnName.trim().toLowerCase()))
const toAdd = GANTT_COLUMNS.filter((c) => !existingNames.has(c.columnName.toLowerCase()))
if (toAdd.length > 0) {
setColumns((prev) => {
const nonEmpty = prev.filter((c) => c.columnName.trim() !== '')
return [...nonEmpty, ...toAdd.map((c) => ({ ...c })), createEmptyColumn()]
})
}
addAutomaticPrimaryKey('gantt', 'Gantt primary key (auto)')
}
const addSchedulerColumns = () => {
const existingNames = new Set(columns.map((c) => c.columnName.trim().toLowerCase()))
const toAdd = SCHEDULER_COLUMNS.filter((c) => !existingNames.has(c.columnName.toLowerCase()))
if (toAdd.length > 0) {
setColumns((prev) => {
const nonEmpty = prev.filter((c) => c.columnName.trim() !== '')
return [...nonEmpty, ...toAdd.map((c) => ({ ...c })), createEmptyColumn()]
})
}
addAutomaticPrimaryKey('scheduler', 'Scheduler primary key (auto)')
}
const importColumnsFromRememberedCreateTable = async () => {
let script = ''
@ -1858,31 +2009,41 @@ const SqlTableDesignerDialog = ({
)}
{!colsLoading && (
<>
<div className="flex items-center justify-between py-2">
<div className="flex items-center gap-2">
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
{translate('::App.SqlQueryManager.AddMultiTenantColumns')}
</Button>
<Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
{translate('::App.SqlQueryManager.AddFullAuditedColumns')}
</Button>
<Button size="xs" variant="solid" color="indigo-600" onClick={addWorkflowColumns}>
{translate('::App.SqlQueryManager.AddWorkflowColumns')}
</Button>
<Button size="xs" variant="solid" color="cyan-600" onClick={addTodoColumns}>
Add Todo Columns
</Button>
<Button
size="xs"
variant="solid"
color="amber-600"
onClick={importColumnsFromRememberedCreateTable}
disabled={isEditMode}
>
CREATE TABLE Kolonları Yapıştır
</Button>
<div className="flex items-start justify-between gap-2 py-2">
<div className="flex flex-col gap-2">
<div className="flex flex-wrap items-center gap-2">
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
{translate('::App.SqlQueryManager.AddMultiTenantColumns')}
</Button>
<Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
{translate('::App.SqlQueryManager.AddFullAuditedColumns')}
</Button>
<Button
size="xs"
variant="solid"
color="amber-600"
onClick={importColumnsFromRememberedCreateTable}
disabled={isEditMode}
>
CREATE TABLE Kolonları Yapıştır
</Button>
</div>
<div className="flex flex-wrap items-center gap-2">
<Button size="xs" variant="solid" color="indigo-600" onClick={addWorkflowColumns}>
{translate('::App.SqlQueryManager.AddWorkflowColumns')}
</Button>
<Button size="xs" variant="solid" color="cyan-600" onClick={addTodoColumns}>
Add Todo Columns
</Button>
<Button size="xs" variant="solid" color="violet-600" onClick={addGanttColumns}>
Add Gantt Columns
</Button>
<Button size="xs" variant="solid" color="teal-600" onClick={addSchedulerColumns}>
Add Scheduler Columns
</Button>
</div>
</div>
<div className="flex items-center gap-2">
<div className="flex flex-col items-stretch gap-2">
<Button
size="xs"
variant="solid"

View file

@ -167,8 +167,10 @@ const useToolbar = ({
visible: true,
disabled: true,
onClick: async () => {
const keys = (await Promise.resolve(getSelectedRowKeys() as any)) as unknown[]
if (!keys?.length) {
const selectedRowKeys = (await Promise.resolve(
getSelectedRowKeys() as any,
)) as unknown[]
if (!selectedRowKeys?.length) {
toast.push(
<Notification type="warning" duration={2000}>
{translate('::ListForms.ListForm.SelectRecord')}
@ -193,6 +195,24 @@ const useToolbar = ({
return
}
if (
!grdOpt.keyFieldName ||
selectedRows.some(
(row) =>
row[grdOpt.keyFieldName!] === undefined || row[grdOpt.keyFieldName!] === null,
)
) {
toast.push(
<Notification type="danger" duration={3000}>
Liste formu anahtar alanı seçili kayıtlarda bulunamadı.
</Notification>,
{ placement: 'bottom-end' },
)
return
}
const keys = selectedRows.map((row) => row[grdOpt.keyFieldName!])
try {
const result = await workflowService.startWorkflow(listFormCode, keys)
showWorkflowToastMessages(result)
@ -226,8 +246,10 @@ const useToolbar = ({
visible: true,
disabled: true,
onClick: async () => {
const keys = (await Promise.resolve(getSelectedRowKeys() as any)) as unknown[]
if (!keys?.length) {
const selectedRowKeys = (await Promise.resolve(
getSelectedRowKeys() as any,
)) as unknown[]
if (!selectedRowKeys?.length) {
toast.push(
<Notification type="warning" duration={2000}>
{translate('::ListForms.ListForm.SelectRecord')}
@ -258,6 +280,24 @@ const useToolbar = ({
return
}
if (
!grdOpt.keyFieldName ||
selectedRows.some(
(row) =>
row[grdOpt.keyFieldName!] === undefined || row[grdOpt.keyFieldName!] === null,
)
) {
toast.push(
<Notification type="danger" duration={3000}>
Liste formu anahtar alanı seçili kayıtlarda bulunamadı.
</Notification>,
{ placement: 'bottom-end' },
)
return
}
const keys = selectedRows.map((row) => row[grdOpt.keyFieldName!])
setToolbarModalData({
open: true,
content: (