Wizard menu sıra numarası sabit tutuldu

This commit is contained in:
Sedat ÖZTÜRK 2026-07-29 09:11:09 +03:00
parent d85cd56464
commit 55793e3db0
10 changed files with 172 additions and 24 deletions

View file

@ -9,6 +9,7 @@ public class ListFormWizardDto
public string WizardName { get; set; } public string WizardName { get; set; }
public string ListFormCode { get; set; } public string ListFormCode { get; set; }
public string MenuCode { get; set; } public string MenuCode { get; set; }
public int MenuOrder { get; set; }
public bool IsTenant { get; set; } public bool IsTenant { get; set; }
public bool IsBranch { get; set; } public bool IsBranch { get; set; }

View file

@ -195,6 +195,8 @@ public class ListFormWizardAppService(
//Menu //Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0; 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)); var existingMenu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code));
if (existingMenu == null) if (existingMenu == null)
{ {
@ -210,10 +212,15 @@ public class ListFormWizardAppService(
CssClass = null, CssClass = null,
Url = WizardConsts.MenuUrl(code), Url = WizardConsts.MenuUrl(code),
RequiredPermissionName = permRead.Name, RequiredPermissionName = permRead.Name,
Order = maxChildOrder + 1, Order = menuOrder,
}, autoSave: false); }, autoSave: false);
inserted.MenuCodes.Add(code); inserted.MenuCodes.Add(code);
} }
else if (existingMenu.Order != menuOrder)
{
existingMenu.Order = menuOrder;
await repoMenu.UpdateAsync(existingMenu, autoSave: false);
}
//DataSource kodu ile iligli kod blogu //DataSource kodu ile iligli kod blogu
var dataSourceQueryable = await repoDataSource.GetQueryableAsync(); var dataSourceQueryable = await repoDataSource.GetQueryableAsync();
@ -643,6 +650,7 @@ public class ListFormWizardAppService(
{ {
var ins = seed.InsertedRecords; var ins = seed.InsertedRecords;
var listFormCode = seed.Wizard?.ListFormCode; var listFormCode = seed.Wizard?.ListFormCode;
var menuParentCode = seed.Wizard?.MenuParentCode;
// ListForm ve alanları her zaman sil (wizard bunları her zaman oluşturur) // ListForm ve alanları her zaman sil (wizard bunları her zaman oluşturur)
if (!string.IsNullOrWhiteSpace(listFormCode)) if (!string.IsNullOrWhiteSpace(listFormCode))
@ -674,6 +682,17 @@ public class ListFormWizardAppService(
// Permission Groups // Permission Groups
foreach (var name in ins.PermissionGroupNames) 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); var pg = await repoPermGroup.FirstOrDefaultAsync(a => a.Name == name);
if (pg != null) await repoPermGroup.DeleteAsync(pg, autoSave: true); if (pg != null) await repoPermGroup.DeleteAsync(pg, autoSave: true);
} }
@ -681,6 +700,12 @@ public class ListFormWizardAppService(
// Menus // Menus
foreach (var code in ins.MenuCodes) 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); var m = await repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (m != null) await repoMenu.DeleteAsync(m, autoSave: true); if (m != null) await repoMenu.DeleteAsync(m, autoSave: true);
} }
@ -696,6 +721,10 @@ public class ListFormWizardAppService(
var appName = PlatformConsts.AppName; var appName = PlatformConsts.AppName;
foreach (var key in ins.LanguageKeys) 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); var lk = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == appName && a.Key == key);
if (lk == null) continue; if (lk == null) continue;

View file

@ -17796,6 +17796,18 @@
"en": "Auto-derived, editable", "en": "Auto-derived, editable",
"tr": "Otomatik oluşturulur, düzenlenebilir" "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", "resourceName": "Platform",
"key": "ListForms.Wizard.Add", "key": "ListForms.Wizard.Add",

View file

@ -3,6 +3,7 @@
"WizardName": "Approval", "WizardName": "Approval",
"ListFormCode": "App.Wizard.Approval", "ListFormCode": "App.Wizard.Approval",
"MenuCode": "App.Wizard.Approval", "MenuCode": "App.Wizard.Approval",
"MenuOrder": 2,
"IsTenant": true, "IsTenant": true,
"IsBranch": false, "IsBranch": false,
"IsOrganizationUnit": false, "IsOrganizationUnit": false,

View file

@ -3,6 +3,7 @@
"WizardName": "Gantt", "WizardName": "Gantt",
"ListFormCode": "App.Wizard.Gantt", "ListFormCode": "App.Wizard.Gantt",
"MenuCode": "App.Wizard.Gantt", "MenuCode": "App.Wizard.Gantt",
"MenuOrder": 3,
"IsTenant": false, "IsTenant": false,
"IsBranch": false, "IsBranch": false,
"IsOrganizationUnit": false, "IsOrganizationUnit": false,
@ -84,23 +85,6 @@
"DisplayExpr": "Name", "DisplayExpr": "Name",
"LookupQuery": "" "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", "FieldName": "Title",
"CaptionName": "App.Listform.ListformField.Title", "CaptionName": "App.Listform.ListformField.Title",
@ -118,6 +102,23 @@
"DisplayExpr": "Name", "DisplayExpr": "Name",
"LookupQuery": "" "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", "FieldName": "StartDate",
"CaptionName": "App.Listform.ListformField.StartDate", "CaptionName": "App.Listform.ListformField.StartDate",

View file

@ -3,6 +3,7 @@
"WizardName": "Todo", "WizardName": "Todo",
"ListFormCode": "App.Wizard.Todo", "ListFormCode": "App.Wizard.Todo",
"MenuCode": "App.Wizard.Todo", "MenuCode": "App.Wizard.Todo",
"MenuOrder": 1,
"IsTenant": true, "IsTenant": true,
"IsBranch": false, "IsBranch": false,
"IsOrganizationUnit": false, "IsOrganizationUnit": false,
@ -15,8 +16,8 @@
"Grid": true, "Grid": true,
"Card": true, "Card": true,
"Pivot": true, "Pivot": true,
"Chart": true,
"Tree": false, "Tree": false,
"Chart": true,
"Gantt": false, "Gantt": false,
"Scheduler": false, "Scheduler": false,
"Todo": true, "Todo": true,
@ -39,9 +40,61 @@
"SelectCommand": "Pro_T_Todo", "SelectCommand": "Pro_T_Todo",
"KeyFieldName": "Id", "KeyFieldName": "Id",
"KeyFieldDbSourceType": 9, "KeyFieldDbSourceType": 9,
"TreeOptionDto": {}, "TreeOptionDto": {
"GanttOptionDto": {}, "KeyExpr": null,
"SchedulerOptionDto": {}, "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": { "TodoOptionDto": {
"TitleExpr": "Title", "TitleExpr": "Title",
"StatusExpr": "Status", "StatusExpr": "Status",

View file

@ -256,6 +256,8 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
// Menu // Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0; 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); var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (existingMenu == null) if (existingMenu == null)
{ {
@ -271,9 +273,14 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
CssClass = null, CssClass = null,
Url = WizardConsts.MenuUrl(code), Url = WizardConsts.MenuUrl(code),
RequiredPermissionName = permRead.Name, RequiredPermissionName = permRead.Name,
Order = maxChildOrder + 1, Order = menuOrder,
}, autoSave: true); }, autoSave: true);
} }
else if (existingMenu.Order != menuOrder)
{
existingMenu.Order = menuOrder;
await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
}
// DataSource // DataSource
var existingDataSource = await _repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode); var existingDataSource = await _repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode);

View file

@ -31,6 +31,7 @@ export interface ListFormWizardDto {
wizardName: string wizardName: string
listFormCode: string listFormCode: string
menuCode: string menuCode: string
menuOrder: number
isTenant: boolean isTenant: boolean
isBranch: boolean isBranch: boolean
isOrganizationUnit: boolean isOrganizationUnit: boolean

View file

@ -52,6 +52,7 @@ const initialValues: ListFormWizardDto = {
wizardName: '', wizardName: '',
listFormCode: '', listFormCode: '',
menuCode: '', menuCode: '',
menuOrder: 0,
isTenant: false, isTenant: false,
isBranch: false, isBranch: false,
isOrganizationUnit: false, isOrganizationUnit: false,
@ -165,6 +166,7 @@ const initialValues: ListFormWizardDto = {
const step1ValidationSchema = Yup.object().shape({ const step1ValidationSchema = Yup.object().shape({
wizardName: Yup.string().required(), wizardName: Yup.string().required(),
menuCode: Yup.string().required(), menuCode: Yup.string().required(),
menuOrder: Yup.number().integer().min(1).required(),
permissionGroupName: Yup.string().required(), permissionGroupName: Yup.string().required(),
languageTextMenuEn: Yup.string().required(), languageTextMenuEn: Yup.string().required(),
languageTextMenuTr: Yup.string().required(), languageTextMenuTr: Yup.string().required(),
@ -452,6 +454,10 @@ const Wizard = () => {
wizardName: w.wizardName ?? '', wizardName: w.wizardName ?? '',
listFormCode: w.listFormCode ?? '', listFormCode: w.listFormCode ?? '',
menuCode: w.menuCode ?? '', menuCode: w.menuCode ?? '',
menuOrder:
w.menuOrder && w.menuOrder > 0
? w.menuOrder
: (rawMenuItems.find((item) => item.code === w.menuCode)?.order ?? 1),
isTenant: w.isTenant ?? false, isTenant: w.isTenant ?? false,
isBranch: w.isBranch ?? false, isBranch: w.isBranch ?? false,
isOrganizationUnit: w.isOrganizationUnit ?? false, isOrganizationUnit: w.isOrganizationUnit ?? false,
@ -649,7 +655,16 @@ const Wizard = () => {
} }
const handleMenuParentChange = (code: string) => { 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) const selectedMenu = rawMenuItems.find((item) => item.code === code)
formikRef.current?.setFieldValue('menuParentIcon', selectedMenu?.icon ?? '') formikRef.current?.setFieldValue('menuParentIcon', selectedMenu?.icon ?? '')
formikRef.current?.setFieldValue('languageTextMenuParentEn', selectedMenu?.menuTextEn ?? '') formikRef.current?.setFieldValue('languageTextMenuParentEn', selectedMenu?.menuTextEn ?? '')
@ -673,6 +688,11 @@ const Wizard = () => {
shortName?: string shortName?: string
}) => { }) => {
formikRef.current?.setFieldValue('menuParentCode', menu.code) 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('menuParentIcon', menu.icon ?? '')
formikRef.current?.setFieldValue('languageTextMenuParentEn', menu.menuTextEn) formikRef.current?.setFieldValue('languageTextMenuParentEn', menu.menuTextEn)
formikRef.current?.setFieldValue('languageTextMenuParentTr', menu.menuTextTr) formikRef.current?.setFieldValue('languageTextMenuParentTr', menu.menuTextTr)

View file

@ -615,6 +615,29 @@ const WizardStep1 = ({
/> />
</FormItem> </FormItem>
{/* Menu Order */}
<FormItem
label={translate('::ListForms.Wizard.Step1.MenuOrder') || 'Menu Order'}
asterisk={true}
invalid={!!(errors.menuOrder && touched.menuOrder)}
errorMessage={errors.menuOrder}
extra={
<span className="text-xs ml-2 text-gray-400">
{translate('::ListForms.Wizard.Step1.MenuOrderHint') ||
'Automatically assigned, editable'}
</span>
}
>
<Field
type="number"
min={1}
step={1}
autoComplete="off"
name="menuOrder"
component={Input}
/>
</FormItem>
{/* Menu Icon */} {/* Menu Icon */}
<FormItem <FormItem
label={translate('::ListForms.Wizard.Step1.MenuIcon')} label={translate('::ListForms.Wizard.Step1.MenuIcon')}