From ec6b35d6d57b1f0335ada9fdbbb2d050c99710c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?=
<76204082+iamsedatozturk@users.noreply.github.com>
Date: Mon, 24 Aug 2026 15:00:31 +0300
Subject: [PATCH] =?UTF-8?q?Men=C3=BCs=C3=BCz=20Wizard=20ekleme?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ListForms/Wizard/ListFormWizardDto.cs | 8 +
.../ListForms/ListFormWizardAppService.cs | 132 ++++---
.../Migrations/LanguagesData.json | 22 +-
.../Migrations/MenusData.json | 20 +-
.../Migrations/WizardDataSeeder.cs | 118 +++---
ui/src/proxy/admin/wizard/models.ts | 6 +
ui/src/views/admin/listForm/wizard/Wizard.tsx | 32 +-
.../listForm/wizard/WizardFileManager.tsx | 188 +++++----
.../listForm/wizard/WizardStepComponent.tsx | 2 +-
.../listForm/wizard/WizardStepDeploy.tsx | 11 +-
.../admin/listForm/wizard/WizardStepMenu.tsx | 369 ++++++++++--------
11 files changed, 517 insertions(+), 391 deletions(-)
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 980bd305..4b1479c7 100644
--- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs
+++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs
@@ -27,6 +27,14 @@ public class ListFormWizardDto
public string MenuCode { get; set; }
public int MenuOrder { get; set; }
+ ///
+ /// Wizard'in menuye eklenip eklenmeyecegi. False ise menu (ve gerekiyorsa ust menu) kaydi
+ /// hic uretilmez; ListForm, permission ve dil anahtarlari her iki durumda da uretilir.
+ /// Menusuz wizard'lar baska bir ekranda SubGrid/parca olarak kullanilir.
+ /// Varsayilan true oldugu icin bu alani tasimayan eski seed dosyalari eskisi gibi calisir.
+ ///
+ public bool CreateMenu { get; set; } = true;
+
/// Custom yolunda menuye baglanacak CustomComponent'in adi.
public string CustomComponentName { get; set; }
diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
index 9c0b8179..12cd38f7 100644
--- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
+++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs
@@ -133,7 +133,11 @@ public class ListFormWizardAppService(
}
//Permission Group
- var groupName = input.PermissionGroupName ?? PlatformConsts.AppName;
+ // Menuye eklenmeyen wizard'da izin grubu sorulmaz; bos geldiginde varsayilan gruba dusar.
+ var groupName = string.IsNullOrWhiteSpace(input.PermissionGroupName)
+ ? PlatformConsts.AppName
+ : input.PermissionGroupName.Trim();
+ input.PermissionGroupName = groupName;
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
@@ -208,69 +212,75 @@ public class ListFormWizardAppService(
await permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: false);
}
- //Menu Parent
- var menuQueryable = await repoMenu.GetQueryableAsync();
- var menuParent = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == input.MenuParentCode));
- if (menuParent == null)
- {
- var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
- var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
- ? input.MenuParentIcon
- : !string.IsNullOrWhiteSpace(input.MenuIcon)
- ? input.MenuIcon
- : WizardConsts.MenuIcon;
- await CreateLangKey(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
- menuParent = await repoMenu.InsertAsync(new Menu
- {
- Code = input.MenuParentCode,
- DisplayName = input.MenuParentCode,
- IsDisabled = false,
- ShortName = input.MenuParentShortName?.Trim(),
- Icon = menuParentIcon,
- Order = maxRootOrder + 1,
- }, autoSave: false);
- inserted.MenuCodes.Add(input.MenuParentCode);
- }
- else if (string.IsNullOrWhiteSpace(menuParent.ShortName) &&
- !string.IsNullOrWhiteSpace(input.MenuParentShortName))
- {
- menuParent.ShortName = input.MenuParentShortName.Trim();
- await repoMenu.UpdateAsync(menuParent, autoSave: false);
- }
+ // Menunun acacagi adres: Custom yolunda CustomComponent'in kendi rotasi (bu rota
+ // mapCustomComponentRoutes tarafindan zaten kayitlidir), aksi halde ListForm adresi.
+ // Seed dosyasindan da gorulebilmesi icin input'a geri yazilir; menu uretilmese de
+ // ListForm'un adresi degismedigi icin ayni sekilde hesaplanir.
+ input.MenuUrl = isCustomComponent ? customRoutePath : WizardConsts.MenuUrl(code);
- //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;
- // Menünün açacağı adres: Custom yolunda CustomComponent'in kendi rotası (bu rota
- // mapCustomComponentRoutes tarafından zaten kayıtlıdır), aksi halde ListForm adresi.
- // Seed dosyasından da görülebilmesi için input'a geri yazılır.
- var menuUrl = isCustomComponent ? customRoutePath : WizardConsts.MenuUrl(code);
- input.MenuUrl = menuUrl;
- var existingMenu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code));
- if (existingMenu == null)
+ // Menusuz wizard: menu (ve ust menu) kaydi hic uretilmez, geri kalan her sey aynidir.
+ if (input.CreateMenu)
{
- await repoMenu.InsertAsync(new Menu
+ //Menu Parent
+ var menuQueryable = await repoMenu.GetQueryableAsync();
+ var menuParent = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == input.MenuParentCode));
+ if (menuParent == null)
{
- Code = code,
- DisplayName = nameLangKey,
- IsDisabled = false,
- ParentCode = menuParent.Code,
- Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
- Target = null,
- ElementId = null,
- CssClass = null,
- Url = menuUrl,
- RequiredPermissionName = permRead.Name,
- Order = menuOrder,
- }, autoSave: false);
- inserted.MenuCodes.Add(code);
- }
- else if (existingMenu.Order != menuOrder || existingMenu.Url != menuUrl)
- {
- existingMenu.Order = menuOrder;
- existingMenu.Url = menuUrl;
- await repoMenu.UpdateAsync(existingMenu, autoSave: false);
+ var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
+ var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
+ ? input.MenuParentIcon
+ : !string.IsNullOrWhiteSpace(input.MenuIcon)
+ ? input.MenuIcon
+ : WizardConsts.MenuIcon;
+ await CreateLangKey(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
+ menuParent = await repoMenu.InsertAsync(new Menu
+ {
+ Code = input.MenuParentCode,
+ DisplayName = input.MenuParentCode,
+ IsDisabled = false,
+ ShortName = input.MenuParentShortName?.Trim(),
+ Icon = menuParentIcon,
+ Order = maxRootOrder + 1,
+ }, autoSave: false);
+ inserted.MenuCodes.Add(input.MenuParentCode);
+ }
+ else if (string.IsNullOrWhiteSpace(menuParent.ShortName) &&
+ !string.IsNullOrWhiteSpace(input.MenuParentShortName))
+ {
+ menuParent.ShortName = input.MenuParentShortName.Trim();
+ await repoMenu.UpdateAsync(menuParent, autoSave: false);
+ }
+
+ //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 menuUrl = input.MenuUrl;
+ var existingMenu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code));
+ if (existingMenu == null)
+ {
+ await repoMenu.InsertAsync(new Menu
+ {
+ Code = code,
+ DisplayName = nameLangKey,
+ IsDisabled = false,
+ ParentCode = menuParent.Code,
+ Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
+ Target = null,
+ ElementId = null,
+ CssClass = null,
+ Url = menuUrl,
+ RequiredPermissionName = permRead.Name,
+ Order = menuOrder,
+ }, autoSave: false);
+ inserted.MenuCodes.Add(code);
+ }
+ else if (existingMenu.Order != menuOrder || existingMenu.Url != menuUrl)
+ {
+ existingMenu.Order = menuOrder;
+ existingMenu.Url = menuUrl;
+ await repoMenu.UpdateAsync(existingMenu, autoSave: false);
+ }
}
// Custom yolunda ListForm ekosistemi üretilmez: veri kaynağı, ListForm, alanlar,
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
index ae68e954..1a484d4f 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
@@ -1503,8 +1503,8 @@
{
"resourceName": "Platform",
"key": "App.Listforms.Listform",
- "en": "Listforms",
- "tr": "Listformlar"
+ "en": "ListForm Components",
+ "tr": "List Form Bileşenleri"
},
{
"resourceName": "Platform",
@@ -16326,6 +16326,18 @@
"en": "Automatically assigned, editable",
"tr": "Otomatik atanır, düzenlenebilir"
},
+ {
+ "resourceName": "Platform",
+ "key": "App.WizardStep1.CreateMenu",
+ "en": "Show in menu",
+ "tr": "Menüde göster"
+ },
+ {
+ "resourceName": "Platform",
+ "key": "App.WizardStep1.CreateMenuHint",
+ "en": "If off, no menu record is created; the list is still usable as a sub grid or component",
+ "tr": "Kapalıysa menü kaydı oluşturulmaz; liste yine SubGrid veya bileşen olarak kullanılabilir"
+ },
{
"resourceName": "Platform",
"key": "App.Platform.Add",
@@ -16836,12 +16848,6 @@
"en": "Decides whether the wizard generates a list form or links an existing custom component",
"tr": "Wizard'ın liste formu mu üreteceğini yoksa mevcut bir özel bileşene mi bağlanacağını belirler"
},
- {
- "resourceName": "Platform",
- "key": "App.WizardStepComponent.ListComponent",
- "en": "List Component",
- "tr": "Liste Bileşeni"
- },
{
"resourceName": "Platform",
"key": "App.WizardStepComponent.ListComponentHint",
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json
index b5ed9898..a61b3838 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json
@@ -797,16 +797,6 @@
"RequiredPermissionName": "App.DataSource",
"IsDisabled": false
},
- {
- "ParentCode": "App.Saas",
- "Code": "App.Listforms.Listform",
- "DisplayName": "App.Listforms.Listform",
- "Order": 9,
- "Url": "/admin/list/App.Listforms.Listform",
- "Icon": "FcDataSheet",
- "RequiredPermissionName": "App.Listforms.Listform",
- "IsDisabled": false
- },
{
"ParentCode": "App.Saas",
"Code": "App.Notifications",
@@ -978,6 +968,16 @@
"RequiredPermissionName": "App.DeveloperKit.Components",
"IsDisabled": false
},
+ {
+ "ParentCode": "App.DeveloperKit",
+ "Code": "App.Listforms.Listform",
+ "DisplayName": "App.Listforms.Listform",
+ "Order": 6,
+ "Url": "/admin/list/App.Listforms.Listform",
+ "Icon": "FcDataSheet",
+ "RequiredPermissionName": "App.Listforms.Listform",
+ "IsDisabled": false
+ },
{
"ParentCode": "App.DeveloperKit",
"Code": "App.Listforms.WizardManager",
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs
index 5d624847..3481eaae 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs
+++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs
@@ -117,8 +117,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
// Zaten seeded mi kontrol et. Custom yolunda ListForm üretilmediği için
// idempotency menü kaydı üzerinden değerlendirilir.
+ // Menusuz Custom wizard'da ne ListForm ne de menu kaydi olustugu icin
+ // idempotency okuma permission'i uzerinden degerlendirilir.
var alreadySeeded = seedFile.Wizard.ComponentKind == WizardComponentKindEnum.Custom
- ? await _repoMenu.AnyAsync(a => a.Code == ResolveMenuCode(seedFile.Wizard))
+ ? seedFile.Wizard.CreateMenu
+ ? await _repoMenu.AnyAsync(a => a.Code == ResolveMenuCode(seedFile.Wizard))
+ : await _repoPerm.AnyAsync(a => a.Name == ResolveMenuCode(seedFile.Wizard))
: await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode);
if (alreadySeeded)
@@ -181,7 +185,9 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
}
// Permission Group
- var groupName = input.PermissionGroupName ?? AppName;
+ // Menuye eklenmeyen wizard'da izin grubu sorulmaz; bos geldiginde varsayilan gruba dusar.
+ var groupName = string.IsNullOrWhiteSpace(input.PermissionGroupName) ? AppName : input.PermissionGroupName.Trim();
+ input.PermissionGroupName = groupName;
if (!await _repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await _repoPermGroup.InsertAsync(
@@ -249,64 +255,68 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
// await _permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: true);
// }
- // Menu Parent
- var menuQueryable = await _repoMenu.GetQueryableAsync();
- var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
- if (menuParent == null)
- {
- var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
- var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
- ? input.MenuParentIcon
- : !string.IsNullOrWhiteSpace(input.MenuIcon)
- ? input.MenuIcon
- : WizardConsts.MenuIcon;
- await CreateLangKeyAsync(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
- menuParent = await _repoMenu.InsertAsync(new Menu
- {
- Code = input.MenuParentCode,
- DisplayName = input.MenuParentCode,
- IsDisabled = false,
- ShortName = input.MenuParentShortName,
- Icon = menuParentIcon,
- Order = maxRootOrder + 1,
- }, autoSave: true);
- }
-
- // 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;
- // Menünün açacağı adres. Custom yolunda normalize edilmis rota, List yolunda
- // MenuUrl alani (tasimayan eski dosyalar icin AppService.Create ile aynı kural).
- var menuUrl = isCustomComponent
+ // Menunun acacagi adres; menu uretilmese de seed dosyasindaki degeri korunur.
+ input.MenuUrl = isCustomComponent
? customRoutePath
: !string.IsNullOrWhiteSpace(input.MenuUrl)
? input.MenuUrl.Trim()
: WizardConsts.MenuUrl(code);
- input.MenuUrl = menuUrl;
- var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
- if (existingMenu == null)
+
+ // Menusuz wizard: menu (ve ust menu) kaydi hic uretilmez.
+ if (input.CreateMenu)
{
- await _repoMenu.InsertAsync(new Menu
+ // Menu Parent
+ var menuQueryable = await _repoMenu.GetQueryableAsync();
+ var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
+ if (menuParent == null)
{
- Code = code,
- DisplayName = nameLangKey,
- IsDisabled = false,
- ParentCode = menuParent.Code,
- Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
- Target = null,
- ElementId = null,
- CssClass = null,
- Url = menuUrl,
- RequiredPermissionName = permRead.Name,
- Order = menuOrder,
- }, autoSave: true);
- }
- else if (existingMenu.Order != menuOrder || existingMenu.Url != menuUrl)
- {
- existingMenu.Order = menuOrder;
- existingMenu.Url = menuUrl;
- await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
+ var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
+ var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
+ ? input.MenuParentIcon
+ : !string.IsNullOrWhiteSpace(input.MenuIcon)
+ ? input.MenuIcon
+ : WizardConsts.MenuIcon;
+ await CreateLangKeyAsync(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
+ menuParent = await _repoMenu.InsertAsync(new Menu
+ {
+ Code = input.MenuParentCode,
+ DisplayName = input.MenuParentCode,
+ IsDisabled = false,
+ ShortName = input.MenuParentShortName,
+ Icon = menuParentIcon,
+ Order = maxRootOrder + 1,
+ }, autoSave: true);
+ }
+
+ // 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 menuUrl = input.MenuUrl;
+ var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
+ if (existingMenu == null)
+ {
+ await _repoMenu.InsertAsync(new Menu
+ {
+ Code = code,
+ DisplayName = nameLangKey,
+ IsDisabled = false,
+ ParentCode = menuParent.Code,
+ Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
+ Target = null,
+ ElementId = null,
+ CssClass = null,
+ Url = menuUrl,
+ RequiredPermissionName = permRead.Name,
+ Order = menuOrder,
+ }, autoSave: true);
+ }
+ else if (existingMenu.Order != menuOrder || existingMenu.Url != menuUrl)
+ {
+ existingMenu.Order = menuOrder;
+ existingMenu.Url = menuUrl;
+ await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
+ }
}
// Custom yolunda ListForm ekosistemi üretilmez.
diff --git a/ui/src/proxy/admin/wizard/models.ts b/ui/src/proxy/admin/wizard/models.ts
index 178246d6..10bf73e0 100644
--- a/ui/src/proxy/admin/wizard/models.ts
+++ b/ui/src/proxy/admin/wizard/models.ts
@@ -47,6 +47,12 @@ export interface ListFormWizardDto {
menuCode: string
menuOrder: number
+ /**
+ * Wizard menüye eklensin mi. False ise menü kaydı hiç üretilmez; ListForm, permission ve
+ * dil anahtarları yine üretilir (SubGrid gibi başka bir ekranın parçası olarak kullanılır).
+ */
+ createMenu: boolean
+
customComponentName: string
/** Menünün açtığı adres; Custom yolunda seçilen bileşenin rota yolu. */
menuUrl: string
diff --git a/ui/src/views/admin/listForm/wizard/Wizard.tsx b/ui/src/views/admin/listForm/wizard/Wizard.tsx
index 88d8354a..e5dd21c5 100644
--- a/ui/src/views/admin/listForm/wizard/Wizard.tsx
+++ b/ui/src/views/admin/listForm/wizard/Wizard.tsx
@@ -55,6 +55,7 @@ const initialValues: ListFormWizardDto = {
listFormCode: '',
menuCode: '',
menuOrder: 0,
+ createMenu: true,
componentKind: WizardComponentKindEnum.List,
customComponentName: '',
menuUrl: '',
@@ -174,7 +175,16 @@ const initialValues: ListFormWizardDto = {
const step1ValidationSchema = Yup.object().shape({
wizardName: Yup.string().required(),
menuCode: Yup.string().required(),
- menuOrder: Yup.number().integer().min(1).required(),
+ // Menüye eklenmeyen wizard'da sıra alanı hiç sorulmaz; zorunluluğu da kalkar.
+ menuOrder: Yup.number()
+ .integer()
+ .when('createMenu', {
+ is: (createMenu: boolean) => createMenu !== false,
+ then: (schema) => schema.min(1).required(),
+ otherwise: (schema) => schema.optional(),
+ }),
+ createMenu: Yup.boolean(),
+ // İzin grubu menüden bağımsızdır: menüsüz wizard'da da izinler üretilir ve bir gruba yazılır.
permissionGroupName: Yup.string().required(),
languageTextMenuEn: Yup.string().required(),
languageTextMenuTr: Yup.string().required(),
@@ -595,6 +605,8 @@ const Wizard = () => {
wizardName: w.wizardName ?? '',
listFormCode: w.listFormCode ?? '',
menuCode: w.menuCode ?? '',
+ // Alanı taşımayan eski seed dosyaları menülü wizard olarak açılır.
+ createMenu: w.createMenu ?? true,
// Bu alanları içermeyen eski seed dosyaları List yolunda açılır.
componentKind: w.componentKind ?? WizardComponentKindEnum.List,
customComponentName: w.customComponentName ?? '',
@@ -830,6 +842,23 @@ const Wizard = () => {
formikRef.current?.setFieldValue('permissionGroupName', rootCode)
}
+ /**
+ * Menüye eklenmeyecek wizard'da menüye özel alanlar temizlenir; aksi halde seed dosyasına
+ * hiç kullanılmayacak üst menü/ikon/sıra değerleri yazılır ve deploy özeti yanıltır.
+ */
+ const handleCreateMenuChange = (createMenu: boolean) => {
+ const formik = formikRef.current
+ formik?.setFieldValue('createMenu', createMenu)
+ if (createMenu) return
+ formik?.setFieldValue('menuParentCode', '')
+ formik?.setFieldValue('menuParentShortName', '')
+ formik?.setFieldValue('menuParentIcon', '')
+ formik?.setFieldValue('languageTextMenuParentEn', '')
+ formik?.setFieldValue('languageTextMenuParentTr', '')
+ formik?.setFieldValue('menuIcon', '')
+ formik?.setFieldValue('menuOrder', 0)
+ }
+
const handleMenuParentChange = (code: string) => {
const formik = formikRef.current
const parentChanged = formik?.values.menuParentCode !== code
@@ -1180,6 +1209,7 @@ const Wizard = () => {
menuTree={menuTree}
isLoadingMenu={isLoadingMenu}
onMenuParentChange={handleMenuParentChange}
+ onCreateMenuChange={handleCreateMenuChange}
onClearMenuParent={() => {
formikRef.current?.setFieldValue('menuParentCode', '')
formikRef.current?.setFieldValue('menuParentShortName', '')
diff --git a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx
index 211234c2..63489805 100644
--- a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx
+++ b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx
@@ -354,11 +354,11 @@ const WizardFileManager = () => {
className="flex flex-col gap-2 p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
>
-
+
{/* 1. satır: wizard adı */}
{f.wizardName || f.fileName}
@@ -368,85 +368,9 @@ const WizardFileManager = () => {
{f.fileName}
-
- {isCustom ? f.customComponentName : f.listFormCode}
-
-
-
-
- {/* 3. satır: yol ayrımı (Custom / List), layout ve menü adresi */}
-
-
- {isCustom ? : }
- {translate(
- isCustom
- ? '::App.WizardStepComponent.CustomComponent'
- : '::App.WizardStepComponent.ListComponent',
- )}
-
-
- {/* Custom yolunda layout kavramı yoktur. */}
- {!isCustom && f.defaultLayout && (
-
- {f.defaultLayout}
-
- )}
-
- {f.menuUrl && (
-
-
- {f.menuUrl}
-
- )}
-
-
- {renderActions(
- f,
- 'flex items-center justify-end gap-1 pt-1 border-t border-gray-200 dark:border-gray-700',
- )}
-
- )
- })}
-
- ) : (
-
- {filteredFiles.map((f) => {
- const Icon = navigationIcon[f.menuIcon] ?? FaDatabase
- const isCustom = f.componentKind === WizardComponentKindEnum.Custom
- return (
-
-
-
-
- {/* 1. satır: wizard adı */}
-
- {f.wizardName || f.fileName}
-
-
- {/* 2. satır: dosya adı ve üretilen kod */}
-
- {f.fileName}
- •
-
- {isCustom ? f.customComponentName : f.listFormCode}
-
-
{/* 3. satır: yol ayrımı (Custom / List), layout ve menü adresi */}
-
+
{
{translate(
isCustom
? '::App.WizardStepComponent.CustomComponent'
- : '::App.WizardStepComponent.ListComponent',
+ : '::App.Listforms.Listform',
)}
+
+ {isCustom ? f.customComponentName : f.listFormCode}
+
+
+
{/* Custom yolunda layout kavramı yoktur. */}
{!isCustom && f.defaultLayout && (
@@ -471,7 +400,10 @@ const WizardFileManager = () => {
)}
{f.menuUrl && (
-
+
{f.menuUrl}
@@ -480,11 +412,103 @@ const WizardFileManager = () => {
- {renderActions(f, 'flex items-center gap-1 shrink-0 sm:ml-3')}
+ {renderActions(
+ f,
+ 'flex items-center justify-end gap-1 pt-1 border-t border-gray-200 dark:border-gray-700',
+ )}
)
})}
+ ) : (
+
+
+
+
+
+ {translate('::App.Platform.Name')}
+
+
+ {translate('::App.FileManager.FileName')}
+
+
+ {translate('::App.Platform.Code')}
+
+
+ {translate('::App.WizardStepComponent.ComponentType')}
+
+
+ {translate('::App.WizardStepComponent.MenuUrl')}
+
+
+ {translate('::App.Platform.Actions')}
+
+
+
+
+ {filteredFiles.map((f) => {
+ const Icon = navigationIcon[f.menuIcon] ?? FaDatabase
+ const isCustom = f.componentKind === WizardComponentKindEnum.Custom
+ return (
+
+
+
+
+
+ {f.wizardName || f.fileName}
+
+
+
+
+ {f.fileName}
+
+
+ {isCustom ? f.customComponentName : f.listFormCode}
+
+
+
+ {isCustom ? : }
+ {translate(
+ isCustom
+ ? '::App.WizardStepComponent.CustomComponent'
+ : '::App.Listforms.Listform',
+ )}
+
+
+
+
+ {f.menuUrl && }
+ {f.menuUrl}
+
+
+
+ {renderActions(f, 'flex items-center justify-end gap-1')}
+
+
+ )
+ })}
+
+
+
)}
diff --git a/ui/src/views/admin/listForm/wizard/WizardStepComponent.tsx b/ui/src/views/admin/listForm/wizard/WizardStepComponent.tsx
index f1a7992a..340a98e9 100644
--- a/ui/src/views/admin/listForm/wizard/WizardStepComponent.tsx
+++ b/ui/src/views/admin/listForm/wizard/WizardStepComponent.tsx
@@ -87,7 +87,7 @@ const WizardStepComponent = ({
}
- title={translate('::App.WizardStepComponent.ListComponent')}
+ title={translate('::App.Listforms.Listform')}
description={translate('::App.WizardStepComponent.ListComponentHint')}
onSelect={() => onKindChange(WizardComponentKindEnum.List)}
/>
diff --git a/ui/src/views/admin/listForm/wizard/WizardStepDeploy.tsx b/ui/src/views/admin/listForm/wizard/WizardStepDeploy.tsx
index f3bd2254..7a524352 100644
--- a/ui/src/views/admin/listForm/wizard/WizardStepDeploy.tsx
+++ b/ui/src/views/admin/listForm/wizard/WizardStepDeploy.tsx
@@ -69,10 +69,13 @@ function buildLogSteps(
translate: (key: string, params?: Record
) => string,
): Omit[] {
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
+ // Menuye eklenmeyen wizard'da menu kaydi uretilmez; adim listesinde de yer almaz.
+ const withoutMenuStep = (steps: Omit[]) =>
+ values.createMenu === false ? steps.filter((step) => step.id !== 2) : steps
// Custom yolunda veri kaynağı / ListForm / alan üretimi hiç çalışmaz.
if (values.componentKind === WizardComponentKindEnum.Custom) {
- return [
+ return withoutMenuStep([
{ id: 1, label: translate('::App.Step4Log.ValidatingConfig') },
{
id: 2,
@@ -99,10 +102,10 @@ function buildLogSteps(
},
{ id: 8, label: translate('::App.Step4Log.Deploying') },
{ id: 9, label: translate('::App.Step4Log.Completed') },
- ]
+ ])
}
- return [
+ return withoutMenuStep([
{ id: 1, label: translate('::App.Step4Log.ValidatingConfig') },
{
id: 2,
@@ -140,7 +143,7 @@ function buildLogSteps(
},
{ id: 8, label: translate('::App.Step4Log.Deploying') },
{ id: 9, label: translate('::App.Step4Log.Completed') },
- ]
+ ])
}
// ─── Mini-components ──────────────────────────────────────────────────────────
diff --git a/ui/src/views/admin/listForm/wizard/WizardStepMenu.tsx b/ui/src/views/admin/listForm/wizard/WizardStepMenu.tsx
index 0d2f5e26..de2400eb 100644
--- a/ui/src/views/admin/listForm/wizard/WizardStepMenu.tsx
+++ b/ui/src/views/admin/listForm/wizard/WizardStepMenu.tsx
@@ -1,4 +1,5 @@
-import { Button, FormItem, Input, Notification, Select, toast } from '@/components/ui'
+import { Button, FormItem, Input, Notification, Select, Switcher, toast } from '@/components/ui'
+import classNames from 'classnames'
import { SelectBoxOption } from '@/types/shared'
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
import { MenuItem } from '@/proxy/menus/menu'
@@ -366,6 +367,8 @@ export interface WizardStepMenuProps {
menuTree: MenuTreeNode[]
isLoadingMenu: boolean
onMenuParentChange: (code: string) => void
+ /** Menuye eklenip eklenmeyecegi; false iken menuye ozel alanlar hic sorulmaz. */
+ onCreateMenuChange: (createMenu: boolean) => void
onClearMenuParent: () => void
onMenuCreated: (menu: {
code: string
@@ -399,6 +402,7 @@ const WizardStepMenu = ({
menuTree,
isLoadingMenu,
onMenuParentChange,
+ onCreateMenuChange,
onClearMenuParent,
onMenuCreated,
onReloadMenu,
@@ -446,16 +450,22 @@ const WizardStepMenu = ({
? `/admin/list/${values.menuCode}`
: ''
+ /**
+ * Menuye eklenmeyen wizard: ListForm, permission ve dil anahtarlari uretilir, menu kaydi
+ * uretilmez. Bu durumda ust menu / ikon / sira / adres alanlari hic sorulmaz.
+ */
+ const createMenu = values.createMenu !== false
+
const step1Missing = [
!wizardName && translate('::App.WizardStep1.WizardName'),
!values.menuCode && translate('::App.WizardStep1.MenuCode'),
// Yup şeması menuOrder >= 1 istiyor; başlangıç değeri 0 olduğu için
// burada kontrol edilmezse "Next" aktif görünüp sessizce çalışmıyordu.
- !(Number(values.menuOrder) >= 1) && translate('::App.WizardStep1.MenuOrder'),
+ createMenu && !(Number(values.menuOrder) >= 1) && translate('::App.WizardStep1.MenuOrder'),
!values.permissionGroupName && translate('::App.WizardStep1.PermissionGroupName'),
!values.languageTextMenuEn && translate('::App.WizardStep4.MenuEn'),
!values.languageTextMenuTr && translate('::App.WizardStep4.MenuTr'),
- !values.menuIcon && translate('::App.WizardStep4.MenuIcon'),
+ createMenu && !values.menuIcon && translate('::App.WizardStep4.MenuIcon'),
].filter(Boolean) as string[]
// Geçersiz ya da çakışan ad deploy'da menü/permission kayıtlarını bozar; adım burada durur.
const step1CanGo = step1Missing.length === 0 && !wizardNameError
@@ -474,169 +484,144 @@ const WizardStepMenu = ({
}
>
- onWizardNameChange(e.target.value.replace(/\s/g, ''))}
- />
+
+
onWizardNameChange(e.target.value.replace(/\s/g, ''))}
+ />
+ {/* Menuye eklenecek mi: kapaliyken menuye ozel alanlarin hicbiri sorulmaz. */}
+
+ {/* Switcher kontrollu kullanimda onChange'e mevcut degeri geri verdigi icin
+ yeni deger burada mevcut degerin tersi olarak hesaplanir. */}
+ onCreateMenuChange(!createMenu)} />
+ onCreateMenuChange(!createMenu)}>
+ {translate('::App.WizardStep1.CreateMenu')}
+
+
+
{/* Menu Code / Menu Text / Permission Group — 2-column grid */}
-
- {/* Col 1 */}
-
- {/* Menu Parent */}
-
- {
- setMenuBeingEdited(null)
- setMenuDialogParentCode(
- values.menuParentCode
- ? findRootCode(rawMenuItems, values.menuParentCode)
- : '',
- )
- setMenuDialogOpen(true)
- }}
- size="xs"
- variant="solid"
- icon={ }
+
+ {/* Col 1 — yalnizca menuye eklenen wizard'da: ust menu agaci */}
+ {createMenu && (
+
+ {/* Menu Parent */}
+
icinde oldugu icin label'in bos alanina tiklamak ilk
+ kontrolu (Add) tetikliyordu; varsayilan davranis burada durdurulur. */
+ e.preventDefault()}
>
- {translate('::App.Platform.Add')}
-
- {values.menuParentCode && (
{
- e.stopPropagation()
- e.preventDefault()
- onClearMenuParent()
+ onClick={() => {
+ setMenuBeingEdited(null)
+ setMenuDialogParentCode(
+ values.menuParentCode
+ ? findRootCode(rawMenuItems, values.menuParentCode)
+ : '',
+ )
+ setMenuDialogOpen(true)
}}
size="xs"
- variant="default"
- icon={ }
- className="!inline-flex !h-auto !items-center !justify-center gap-1 !rounded !px-2 !py-0.5 text-xs text-gray-500 whitespace-nowrap hover:border-red-400 hover:text-red-500 dark:border-gray-600"
+ variant="solid"
+ icon={ }
>
- {translate('::App.Platform.ClearSelection')}
+ {translate('::App.Platform.Add')}
- )}
-
- }
- >
-
- {() => (
-
- )}
-
-
-
-
{
- // Düzenlemede seçili parent'ı değiştirmemek için yalnızca ağaç tazelenir.
- if (menuBeingEdited) {
- await onReloadMenu()
- return
+ {values.menuParentCode && (
+ {
+ e.stopPropagation()
+ e.preventDefault()
+ onClearMenuParent()
+ }}
+ size="xs"
+ variant="default"
+ icon={ }
+ className="!inline-flex !h-auto !items-center !justify-center gap-1 !rounded !px-2 !py-0.5 text-xs text-gray-500 whitespace-nowrap hover:border-red-400 hover:text-red-500 dark:border-gray-600"
+ >
+ {translate('::App.Platform.ClearSelection')}
+
+ )}
+ {/* Menu Code: kullanici duzenlemez, parent satirinin en saginda gosterilir. */}
+
+
}
- await onMenuCreated(menu)
- }}
- />
-
+ >
+
+ {() => (
+
+ )}
+
+
+
+
{
+ // Düzenlemede seçili parent'ı değiştirmemek için yalnızca ağaç tazelenir.
+ if (menuBeingEdited) {
+ await onReloadMenu()
+ return
+ }
+ await onMenuCreated(menu)
+ }}
+ />
+
+ )}
{/* Col 2 */}
- {/* Menu Code */}
-
- {translate('::App.Platform.MenuCodeHint')}
-
- }
- >
-
-
-
- {/* Menu Order */}
-
- {translate('::App.WizardStep1.MenuOrderHint')}
-
- }
- >
-
-
-
- {/* Menu Icon */}
-
-
- {({ field, form }: FieldProps) => (
- form.setFieldValue(field.name, key)}
- invalid={!!(errors.menuIcon && touched.menuIcon)}
- />
- )}
-
-
-
{/* Menu Text (En) */}
- {/* Permission Group Name */}
+ {/* Permission grubu menuden bagimsizdir: izinler her iki durumda da uretilir. */}
- {/* Deploy sonrası menü ikonunun açacağı adres; seçilen yola göre değişir. */}
-
- {translate(
- isCustomComponent
- ? '::App.WizardStepComponent.MenuUrlFromComponent'
- : '::App.WizardStepComponent.MenuUrlFromList',
- )}
-
- }
- >
-
- {menuUrl || '—'}
-
-
+ {createMenu && (
+ <>
+ {/* Menu Icon */}
+
+
+ {({ field, form }: FieldProps) => (
+ form.setFieldValue(field.name, key)}
+ invalid={!!(errors.menuIcon && touched.menuIcon)}
+ />
+ )}
+
+
+
+ {/* Menu Order */}
+
+ {translate('::App.WizardStep1.MenuOrderHint')}
+
+ }
+ >
+
+
+
+ {/* Deploy sonrası menü ikonunun açacağı adres; seçilen yola göre değişir. */}
+
+ {translate(
+ isCustomComponent
+ ? '::App.WizardStepComponent.MenuUrlFromComponent'
+ : '::App.WizardStepComponent.MenuUrlFromList',
+ )}
+
+ }
+ >
+
+ {menuUrl || '—'}
+
+
+ >
+ )}