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 */}