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', + )}
) })}
+ ) : ( +
+ + + + + + + + + + + + + {filteredFiles.map((f) => { + const Icon = navigationIcon[f.menuIcon] ?? FaDatabase + const isCustom = f.componentKind === WizardComponentKindEnum.Custom + return ( + + + + + + + + + ) + })} + +
+ {translate('::App.Platform.Name')} + + {translate('::App.FileManager.FileName')} + + {translate('::App.Platform.Code')} + + {translate('::App.WizardStepComponent.ComponentType')} + + {translate('::App.WizardStepComponent.MenuUrl')} + + {translate('::App.Platform.Actions')} +
+
+ + + {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 */} - - - {values.menuParentCode && ( - )} -
- } - > - - {() => ( - - )} - - - - { - // Düzenlemede seçili parent'ı değiştirmemek için yalnızca ağaç tazelenir. - if (menuBeingEdited) { - await onReloadMenu() - return + {values.menuParentCode && ( + + )} + {/* 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 || '—'} +
+
+ + )}