diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs
index b5057fe5..9c23b272 100644
--- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs
+++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs
@@ -9,6 +9,7 @@ public class ListFormWizardDto
public string WizardName { get; set; }
public string ListFormCode { get; set; }
public string MenuCode { get; set; }
+ public int MenuOrder { get; set; }
public bool IsTenant { get; set; }
public bool IsBranch { get; set; }
diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
index f669d05c..fb850882 100644
--- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
+++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
@@ -195,6 +195,8 @@ public class ListFormWizardAppService(
//Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0;
+ var menuOrder = input.MenuOrder > 0 ? input.MenuOrder : maxChildOrder + 1;
+ input.MenuOrder = menuOrder;
var existingMenu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code));
if (existingMenu == null)
{
@@ -210,10 +212,15 @@ public class ListFormWizardAppService(
CssClass = null,
Url = WizardConsts.MenuUrl(code),
RequiredPermissionName = permRead.Name,
- Order = maxChildOrder + 1,
+ Order = menuOrder,
}, autoSave: false);
inserted.MenuCodes.Add(code);
}
+ else if (existingMenu.Order != menuOrder)
+ {
+ existingMenu.Order = menuOrder;
+ await repoMenu.UpdateAsync(existingMenu, autoSave: false);
+ }
//DataSource kodu ile iligli kod blogu
var dataSourceQueryable = await repoDataSource.GetQueryableAsync();
@@ -643,6 +650,7 @@ public class ListFormWizardAppService(
{
var ins = seed.InsertedRecords;
var listFormCode = seed.Wizard?.ListFormCode;
+ var menuParentCode = seed.Wizard?.MenuParentCode;
// ListForm ve alanları her zaman sil (wizard bunları her zaman oluşturur)
if (!string.IsNullOrWhiteSpace(listFormCode))
@@ -674,6 +682,17 @@ public class ListFormWizardAppService(
// Permission Groups
foreach (var name in ins.PermissionGroupNames)
{
+ // Permission grubu MenuParentCode ile aynıysa diğer Wizard'lar tarafından
+ // paylaşılabilir. Parent menü gibi bu grubu da Wizard silme yaşam
+ // döngüsünün dışında tut.
+ if (string.Equals(name, menuParentCode, StringComparison.OrdinalIgnoreCase))
+ continue;
+
+ // Aynı grupta başka Wizard'lara ait permission kayıtları kaldıysa grup
+ // halen kullanımdadır ve silinmemelidir.
+ if (await repoPerm.AnyAsync(a => a.GroupName == name))
+ continue;
+
var pg = await repoPermGroup.FirstOrDefaultAsync(a => a.Name == name);
if (pg != null) await repoPermGroup.DeleteAsync(pg, autoSave: true);
}
@@ -681,6 +700,12 @@ public class ListFormWizardAppService(
// Menus
foreach (var code in ins.MenuCodes)
{
+ // Parent menüler birden fazla Wizard tarafından paylaşılabilir ve Wizard
+ // silme yaşam döngüsüne ait değildir. Altında başka bir Wizard olmasa bile
+ // parent menüyü koru.
+ if (string.Equals(code, menuParentCode, StringComparison.OrdinalIgnoreCase))
+ continue;
+
var m = await repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (m != null) await repoMenu.DeleteAsync(m, autoSave: true);
}
@@ -696,6 +721,10 @@ public class ListFormWizardAppService(
var appName = PlatformConsts.AppName;
foreach (var key in ins.LanguageKeys)
{
+ // Korunan parent menünün görünen adını da kullanılabilir halde tut.
+ if (string.Equals(key, menuParentCode, StringComparison.OrdinalIgnoreCase))
+ continue;
+
var lk = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == appName && a.Key == key);
if (lk == null) continue;
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
index fb5a6a8e..82f1292d 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
@@ -17796,6 +17796,18 @@
"en": "Auto-derived, editable",
"tr": "Otomatik oluşturulur, düzenlenebilir"
},
+ {
+ "resourceName": "Platform",
+ "key": "ListForms.Wizard.Step1.MenuOrder",
+ "en": "Menu Order",
+ "tr": "Menü Sırası"
+ },
+ {
+ "resourceName": "Platform",
+ "key": "ListForms.Wizard.Step1.MenuOrderHint",
+ "en": "Automatically assigned, editable",
+ "tr": "Otomatik atanır, düzenlenebilir"
+ },
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Add",
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281621_Approval.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281621_Approval.json
index 93259614..2801792d 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281621_Approval.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281621_Approval.json
@@ -3,6 +3,7 @@
"WizardName": "Approval",
"ListFormCode": "App.Wizard.Approval",
"MenuCode": "App.Wizard.Approval",
+ "MenuOrder": 2,
"IsTenant": true,
"IsBranch": false,
"IsOrganizationUnit": false,
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281645_Gantt.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281645_Gantt.json
index 1d48f0b2..46312a08 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281645_Gantt.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607281645_Gantt.json
@@ -3,6 +3,7 @@
"WizardName": "Gantt",
"ListFormCode": "App.Wizard.Gantt",
"MenuCode": "App.Wizard.Gantt",
+ "MenuOrder": 3,
"IsTenant": false,
"IsBranch": false,
"IsOrganizationUnit": false,
@@ -84,23 +85,6 @@
"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",
@@ -118,6 +102,23 @@
"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": "StartDate",
"CaptionName": "App.Listform.ListformField.StartDate",
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607172226_Todo.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607282205_Todo.json
similarity index 87%
rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607172226_Todo.json
rename to api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607282205_Todo.json
index 4816f5ae..14f89cbd 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607172226_Todo.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardData/202607282205_Todo.json
@@ -3,6 +3,7 @@
"WizardName": "Todo",
"ListFormCode": "App.Wizard.Todo",
"MenuCode": "App.Wizard.Todo",
+ "MenuOrder": 1,
"IsTenant": true,
"IsBranch": false,
"IsOrganizationUnit": false,
@@ -15,8 +16,8 @@
"Grid": true,
"Card": true,
"Pivot": true,
- "Chart": true,
"Tree": false,
+ "Chart": true,
"Gantt": false,
"Scheduler": false,
"Todo": true,
@@ -39,9 +40,61 @@
"SelectCommand": "Pro_T_Todo",
"KeyFieldName": "Id",
"KeyFieldDbSourceType": 9,
- "TreeOptionDto": {},
- "GanttOptionDto": {},
- "SchedulerOptionDto": {},
+ "TreeOptionDto": {
+ "KeyExpr": null,
+ "ParentIdExpr": null,
+ "HasItemsExpr": null,
+ "RootValue": null,
+ "ExpandedRowKeys": [],
+ "AutoExpandAll": false,
+ "RecursiveSelection": false,
+ "TitleExpr": null,
+ "StartExpr": null,
+ "EndExpr": null,
+ "ProgressExpr": null
+ },
+ "GanttOptionDto": {
+ "KeyExpr": null,
+ "ParentIdExpr": null,
+ "HasItemsExpr": null,
+ "RootValue": null,
+ "ExpandedRowKeys": [],
+ "AutoExpandAll": false,
+ "RecursiveSelection": false,
+ "TitleExpr": null,
+ "ScaleType": "weeks",
+ "StartExpr": null,
+ "EndExpr": null,
+ "ProgressExpr": null,
+ "AllowEditing": false,
+ "AllowTaskAdding": false,
+ "AllowTaskUpdating": false,
+ "AllowTaskDeleting": false,
+ "AllowDependencyAdding": false,
+ "AllowDependencyDeleting": false,
+ "AllowResourceAdding": false,
+ "AllowResourceDeleting": false
+ },
+ "SchedulerOptionDto": {
+ "TextExpr": null,
+ "StartDateExpr": null,
+ "EndDateExpr": null,
+ "AllDayExpr": null,
+ "RecurrenceRuleExpr": null,
+ "RecurrenceExceptionExpr": null,
+ "StartDayHour": 8,
+ "EndDayHour": 18,
+ "DefaultView": "week",
+ "ShowAllDayPanel": true,
+ "CellDuration": 30,
+ "FirstDayOfWeek": 1,
+ "CrossScrollingEnabled": false,
+ "AllowResizing": false,
+ "AllowDragging": false,
+ "AllowDeleting": false,
+ "AllowEditing": false,
+ "AllowAdding": false
+ },
"TodoOptionDto": {
"TitleExpr": "Title",
"StatusExpr": "Status",
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardDataSeeder.cs b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardDataSeeder.cs
index 4329f8ca..b228d79f 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardDataSeeder.cs
+++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/WizardDataSeeder.cs
@@ -256,6 +256,8 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
// Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0;
+ var menuOrder = input.MenuOrder > 0 ? input.MenuOrder : maxChildOrder + 1;
+ input.MenuOrder = menuOrder;
var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (existingMenu == null)
{
@@ -271,9 +273,14 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
CssClass = null,
Url = WizardConsts.MenuUrl(code),
RequiredPermissionName = permRead.Name,
- Order = maxChildOrder + 1,
+ Order = menuOrder,
}, autoSave: true);
}
+ else if (existingMenu.Order != menuOrder)
+ {
+ existingMenu.Order = menuOrder;
+ await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
+ }
// DataSource
var existingDataSource = await _repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode);
diff --git a/ui/src/proxy/admin/wizard/models.ts b/ui/src/proxy/admin/wizard/models.ts
index 1000f601..b862e837 100644
--- a/ui/src/proxy/admin/wizard/models.ts
+++ b/ui/src/proxy/admin/wizard/models.ts
@@ -31,6 +31,7 @@ export interface ListFormWizardDto {
wizardName: string
listFormCode: string
menuCode: string
+ menuOrder: number
isTenant: boolean
isBranch: boolean
isOrganizationUnit: boolean
diff --git a/ui/src/views/admin/listForm/wizard/Wizard.tsx b/ui/src/views/admin/listForm/wizard/Wizard.tsx
index c2a43516..9fbb945d 100644
--- a/ui/src/views/admin/listForm/wizard/Wizard.tsx
+++ b/ui/src/views/admin/listForm/wizard/Wizard.tsx
@@ -52,6 +52,7 @@ const initialValues: ListFormWizardDto = {
wizardName: '',
listFormCode: '',
menuCode: '',
+ menuOrder: 0,
isTenant: false,
isBranch: false,
isOrganizationUnit: false,
@@ -165,6 +166,7 @@ const initialValues: ListFormWizardDto = {
const step1ValidationSchema = Yup.object().shape({
wizardName: Yup.string().required(),
menuCode: Yup.string().required(),
+ menuOrder: Yup.number().integer().min(1).required(),
permissionGroupName: Yup.string().required(),
languageTextMenuEn: Yup.string().required(),
languageTextMenuTr: Yup.string().required(),
@@ -452,6 +454,10 @@ const Wizard = () => {
wizardName: w.wizardName ?? '',
listFormCode: w.listFormCode ?? '',
menuCode: w.menuCode ?? '',
+ menuOrder:
+ w.menuOrder && w.menuOrder > 0
+ ? w.menuOrder
+ : (rawMenuItems.find((item) => item.code === w.menuCode)?.order ?? 1),
isTenant: w.isTenant ?? false,
isBranch: w.isBranch ?? false,
isOrganizationUnit: w.isOrganizationUnit ?? false,
@@ -649,7 +655,16 @@ const Wizard = () => {
}
const handleMenuParentChange = (code: string) => {
- formikRef.current?.setFieldValue('menuParentCode', code)
+ const formik = formikRef.current
+ const parentChanged = formik?.values.menuParentCode !== code
+ formik?.setFieldValue('menuParentCode', code)
+ if (parentChanged || !formik?.values.menuOrder || formik.values.menuOrder < 1) {
+ const nextMenuOrder =
+ rawMenuItems
+ .filter((item) => (item.parentCode ?? '') === code)
+ .reduce((max, item) => Math.max(max, item.order ?? 0), 0) + 1
+ formik?.setFieldValue('menuOrder', nextMenuOrder)
+ }
const selectedMenu = rawMenuItems.find((item) => item.code === code)
formikRef.current?.setFieldValue('menuParentIcon', selectedMenu?.icon ?? '')
formikRef.current?.setFieldValue('languageTextMenuParentEn', selectedMenu?.menuTextEn ?? '')
@@ -673,6 +688,11 @@ const Wizard = () => {
shortName?: string
}) => {
formikRef.current?.setFieldValue('menuParentCode', menu.code)
+ const nextMenuOrder =
+ rawMenuItems
+ .filter((item) => (item.parentCode ?? '') === menu.code)
+ .reduce((max, item) => Math.max(max, item.order ?? 0), 0) + 1
+ formikRef.current?.setFieldValue('menuOrder', nextMenuOrder)
formikRef.current?.setFieldValue('menuParentIcon', menu.icon ?? '')
formikRef.current?.setFieldValue('languageTextMenuParentEn', menu.menuTextEn)
formikRef.current?.setFieldValue('languageTextMenuParentTr', menu.menuTextTr)
diff --git a/ui/src/views/admin/listForm/wizard/WizardStep1.tsx b/ui/src/views/admin/listForm/wizard/WizardStep1.tsx
index b1d5fbb2..0d8ea56b 100644
--- a/ui/src/views/admin/listForm/wizard/WizardStep1.tsx
+++ b/ui/src/views/admin/listForm/wizard/WizardStep1.tsx
@@ -615,6 +615,29 @@ const WizardStep1 = ({
/>
+ {/* Menu Order */}
+
+ {translate('::ListForms.Wizard.Step1.MenuOrderHint') ||
+ 'Automatically assigned, editable'}
+
+ }
+ >
+
+
+
{/* Menu Icon */}