Wizard Düzenlemeleri

This commit is contained in:
Sedat Öztürk 2026-03-01 00:37:34 +03:00
parent 4b2fceb404
commit 85b498a477
17 changed files with 440 additions and 205 deletions

View file

@ -4,6 +4,6 @@ namespace Sozsoft.Platform.ListForms;
public interface IListFormWizardAppService public interface IListFormWizardAppService
{ {
Task Create(WizardCreateInputDto input); Task Create(ListFormWizardDto input);
} }

View file

@ -4,11 +4,15 @@ using System.Data;
namespace Sozsoft.Platform.ListForms; namespace Sozsoft.Platform.ListForms;
public class WizardCreateInputDto public class ListFormWizardDto
{ {
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 bool IsTenant { get; set; }
public bool IsBranch { get; set; }
public string LanguageTextMenuEn { get; set; } public string LanguageTextMenuEn { get; set; }
public string LanguageTextMenuTr { get; set; } public string LanguageTextMenuTr { get; set; }
public string LanguageTextTitleEn { get; set; } public string LanguageTextTitleEn { get; set; }

View file

@ -22,5 +22,8 @@ public class MenuDto : FullAuditedEntityDto<Guid>
public string UserId { get; set; } // External kullanici id (orn: ali.akman. ihtiyaca gore guid veya int de olabilir) public string UserId { get; set; } // External kullanici id (orn: ali.akman. ihtiyaca gore guid veya int de olabilir)
public string RoleId { get; set; } // External role id (orn: ihracat) public string RoleId { get; set; } // External role id (orn: ihracat)
public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr" public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr"
public string MenuTextTr { get; set; }
public string MenuTextEn { get; set; }
} }

View file

@ -15,7 +15,6 @@ using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using static Sozsoft.Platform.PlatformConsts; using static Sozsoft.Platform.PlatformConsts;
using System.Data; using System.Data;
using Sozsoft.Platform.Data.Seeds;
namespace Sozsoft.Platform.ListForms; namespace Sozsoft.Platform.ListForms;
@ -49,17 +48,19 @@ public class ListFormWizardAppService(
private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage; private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage;
[UnitOfWork] [UnitOfWork]
public async Task Create(WizardCreateInputDto input) public async Task Create(ListFormWizardDto input)
{ {
var listFormCode = input.ListFormCode; var wizardName = input.WizardName.Trim();
var nameLangKey = PlatformConsts.Wizard.WizardKey(listFormCode); var titleLangKey = PlatformConsts.Wizard.WizardKeyTitle(wizardName);
var titleLangKey = PlatformConsts.Wizard.WizardKeyTitle(listFormCode); var nameLangKey = PlatformConsts.Wizard.WizardKey(wizardName);
var code = PlatformConsts.Wizard.WizardKey(listFormCode); var descLangKey = PlatformConsts.Wizard.WizardKeyDesc(wizardName);
var code = PlatformConsts.Wizard.WizardKey(wizardName);
//Dil - Language Keys //Dil - Language Keys
await CreateLangKey(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr); await CreateLangKey(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
await CreateLangKey(titleLangKey, input.LanguageTextTitleEn, input.LanguageTextTitleTr); await CreateLangKey(titleLangKey, input.LanguageTextTitleEn, input.LanguageTextTitleTr);
await CreateLangKey(PlatformConsts.Wizard.WizardKeyDesc(listFormCode), input.LanguageTextDescEn, input.LanguageTextDescTr); await CreateLangKey(descLangKey, input.LanguageTextDescEn, input.LanguageTextDescTr);
//Permission Group //Permission Group
var groupName = input.PermissionGroupName ?? PlatformConsts.AppName; var groupName = input.PermissionGroupName ?? PlatformConsts.AppName;
@ -78,23 +79,23 @@ public class ListFormWizardAppService(
var permRead = existingPerms.FirstOrDefault(a => a.Name == code) ?? var permRead = existingPerms.FirstOrDefault(a => a.Name == code) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: false);
var permCreate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermCreate(listFormCode)) ?? var permCreate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermCreate(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermUpdate(listFormCode)) ?? var permUpdate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermUpdate(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
var permDelete = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermDelete(listFormCode)) ?? var permDelete = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermDelete(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
var permExport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermExport(listFormCode)) ?? var permExport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermExport(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
var permImport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermImport(listFormCode)) ?? var permImport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermImport(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermImport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyImport, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermImport(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
var PermNote = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermNote(listFormCode)) ?? var permNote = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermNote(wizardName)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermNote(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyNote, true, MultiTenancySides.Both), autoSave: false); await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermNote(wizardName), permRead.Name, PlatformConsts.Wizard.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
// Permission Grants - Bulk Insert // Permission Grants - Bulk Insert
var adminUserName = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue; var adminUserName = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
@ -108,6 +109,8 @@ public class ListFormWizardAppService(
new PermissionGrant(Guid.NewGuid(), permUpdate.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName), new PermissionGrant(Guid.NewGuid(), permUpdate.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permDelete.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName), new PermissionGrant(Guid.NewGuid(), permDelete.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permExport.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName), new PermissionGrant(Guid.NewGuid(), permExport.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permImport.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permNote.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
], autoSave: false); ], autoSave: false);
//Menu Parent //Menu Parent
@ -115,11 +118,11 @@ public class ListFormWizardAppService(
var menuParent = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == input.MenuParentCode)); var menuParent = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == input.MenuParentCode));
if (menuParent == null) if (menuParent == null)
{ {
await CreateLangKey(PlatformConsts.Wizard.WizardKeyParent(listFormCode), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr); await CreateLangKey(PlatformConsts.Wizard.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
menuParent = await repoMenu.InsertAsync(new Menu menuParent = await repoMenu.InsertAsync(new Menu
{ {
Code = input.MenuParentCode, Code = input.MenuParentCode,
DisplayName = PlatformConsts.Wizard.WizardKeyParent(listFormCode), DisplayName = PlatformConsts.Wizard.WizardKeyParent(wizardName),
IsDisabled = false, IsDisabled = false,
}, autoSave: false); }, autoSave: false);
} }
@ -136,7 +139,7 @@ public class ListFormWizardAppService(
Target = null, Target = null,
ElementId = null, ElementId = null,
CssClass = null, CssClass = null,
Url = PlatformConsts.Wizard.MenuUrl(listFormCode), Url = PlatformConsts.Wizard.MenuUrl(code),
RequiredPermissionName = permRead.Name RequiredPermissionName = permRead.Name
}, autoSave: false); }, autoSave: false);
@ -187,14 +190,14 @@ public class ListFormWizardAppService(
ShowNote = true, ShowNote = true,
LayoutJson = Wizard.DefaultLayoutJson(), LayoutJson = Wizard.DefaultLayoutJson(),
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
ListFormCode = listFormCode, ListFormCode = input.ListFormCode,
Name = nameLangKey, Name = nameLangKey,
Title = titleLangKey, Title = titleLangKey,
Description = descLangKey,
DataSourceCode = input.DataSourceCode, DataSourceCode = input.DataSourceCode,
IsTenant = false, IsTenant = input.IsTenant,
IsBranch = false, IsBranch = input.IsBranch,
IsOrganizationUnit = false, IsOrganizationUnit = false,
Description = Wizard.WizardKeyDesc(listFormCode),
SelectCommandType = input.SelectCommandType, SelectCommandType = input.SelectCommandType,
SelectCommand = input.SelectCommand, SelectCommand = input.SelectCommand,
KeyFieldName = input.KeyFieldName, KeyFieldName = input.KeyFieldName,
@ -207,11 +210,11 @@ public class ListFormWizardAppService(
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }), GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = Wizard.DefaultSelectionSingleJson, SelectionJson = Wizard.DefaultSelectionSingleJson,
ColumnOptionJson = Wizard.DefaultColumnOptionJson(), ColumnOptionJson = Wizard.DefaultColumnOptionJson(),
PermissionJson = Wizard.DefaultPermissionJson(listFormCode), PermissionJson = Wizard.DefaultPermissionJson(code),
DeleteCommand = Wizard.DefaultDeleteCommand(nameof(TableNameEnum.Country)), DeleteCommand = Wizard.DefaultDeleteCommand(nameof(TableNameEnum.Country)),
DeleteFieldsDefaultValueJson = Wizard.DefaultDeleteFieldsDefaultValueJson(), DeleteFieldsDefaultValueJson = Wizard.DefaultDeleteFieldsDefaultValueJson(input.KeyFieldDbSourceType),
PagerOptionJson = Wizard.DefaultPagerOptionJson, PagerOptionJson = Wizard.DefaultPagerOptionJson,
EditingOptionJson = Wizard.DefaultEditingOptionJson(listFormCode, 600, 500, true, true, true, true, false), EditingOptionJson = Wizard.DefaultEditingOptionJson(titleLangKey, 600, 500, true, true, true, true, false),
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null, EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
}, autoSave: true); }, autoSave: true);
@ -224,7 +227,7 @@ public class ListFormWizardAppService(
fieldOrder++; fieldOrder++;
await repoListFormField.InsertAsync(new ListFormField await repoListFormField.InsertAsync(new ListFormField
{ {
ListFormCode = listFormCode, ListFormCode = input.ListFormCode,
FieldName = item.DataField, FieldName = item.DataField,
CaptionName = item.DataField, CaptionName = item.DataField,
Visible = true, Visible = true,

View file

@ -14,6 +14,7 @@ using Volo.Abp.Domain.Repositories;
using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement; using Volo.Abp.TenantManagement;
using static Sozsoft.Platform.Data.Seeds.SeedConsts; using static Sozsoft.Platform.Data.Seeds.SeedConsts;
using Sozsoft.Languages;
namespace Sozsoft.Platform.Menus; namespace Sozsoft.Platform.Menus;
@ -26,18 +27,21 @@ public class MenuAppService : CrudAppService<
{ {
private readonly IRepository<Menu, Guid> _menuRepository; private readonly IRepository<Menu, Guid> _menuRepository;
private readonly IRepository<LanguageKey, Guid> _repositoryKey; private readonly IRepository<LanguageKey, Guid> _repositoryKey;
private readonly IRepository<LanguageText, Guid> _repositoryText;
private readonly ITenantRepository _tenantRepository; private readonly ITenantRepository _tenantRepository;
private readonly IPermissionDefinitionRecordRepository _permissionRepository; private readonly IPermissionDefinitionRecordRepository _permissionRepository;
public MenuAppService( public MenuAppService(
IRepository<Menu, Guid> menuRepository, IRepository<Menu, Guid> menuRepository,
IRepository<LanguageKey, Guid> languageKeyRepository, IRepository<LanguageKey, Guid> languageKeyRepository,
IRepository<LanguageText, Guid> languageTextRepository,
ITenantRepository tenantRepository, ITenantRepository tenantRepository,
IPermissionDefinitionRecordRepository permissionRepository IPermissionDefinitionRecordRepository permissionRepository
) : base(menuRepository) ) : base(menuRepository)
{ {
_menuRepository = menuRepository; _menuRepository = menuRepository;
_repositoryKey = languageKeyRepository; _repositoryKey = languageKeyRepository;
_repositoryText = languageTextRepository;
_tenantRepository = tenantRepository; _tenantRepository = tenantRepository;
_permissionRepository = permissionRepository; _permissionRepository = permissionRepository;
@ -88,7 +92,7 @@ public class MenuAppService : CrudAppService<
query = ApplyPaging(query, input); query = ApplyPaging(query, input);
var items = await AsyncExecuter.ToListAsync(query); var items = await AsyncExecuter.ToListAsync(query);
// Tüm unique permission'ları topla // Tüm unique permission'ları topla
var uniquePermissions = items var uniquePermissions = items
.Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName)) .Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName))
@ -115,7 +119,7 @@ public class MenuAppService : CrudAppService<
// Sadece yetkili menüleri filtrele // Sadece yetkili menüleri filtrele
entities = items entities = items
.Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) || .Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) ||
grantedPermissions.Contains(item.RequiredPermissionName)) grantedPermissions.Contains(item.RequiredPermissionName))
.ToList(); .ToList();
@ -183,12 +187,12 @@ public class MenuAppService : CrudAppService<
// Tüm DisplayName'leri topla // Tüm DisplayName'leri topla
var displayNames = inputs.Select(x => x.DisplayName).Distinct().ToList(); var displayNames = inputs.Select(x => x.DisplayName).Distinct().ToList();
// Mevcut key'leri tek sorguda getir // Mevcut key'leri tek sorguda getir
var existingKeys = await AsyncExecuter.ToListAsync( var existingKeys = await AsyncExecuter.ToListAsync(
(await _repositoryKey.GetQueryableAsync()) (await _repositoryKey.GetQueryableAsync())
.Where(a => displayNames.Contains(a.Key) && a.ResourceName == PlatformConsts.AppName)); .Where(a => displayNames.Contains(a.Key) && a.ResourceName == PlatformConsts.AppName));
var existingKeyNames = existingKeys.Select(x => x.Key).ToHashSet(); var existingKeyNames = existingKeys.Select(x => x.Key).ToHashSet();
// Eksik key'leri toplu ekle // Eksik key'leri toplu ekle
@ -244,4 +248,68 @@ public class MenuAppService : CrudAppService<
return entityDtos; return entityDtos;
} }
public async Task<MenuDto> CreateWithLanguageKeyTextAsync(MenuDto input)
{
await CheckCreatePolicyAsync();
//Dil Key oluşturuluyor.
var keyExists = await _repositoryKey.AnyAsync(
a => a.Key == input.Code &&
a.ResourceName == PlatformConsts.AppName);
if (!keyExists)
{
await _repositoryKey.InsertAsync(new LanguageKey
{
Key = input.Code,
ResourceName = PlatformConsts.AppName
}, autoSave: true);
}
// English text oluşturuluyor veya güncelleniyor.
var existingEnText = await _repositoryText.FirstOrDefaultAsync(
a => a.CultureName == "en" &&
a.Key == input.Code &&
a.ResourceName == PlatformConsts.AppName);
if (existingEnText != null)
{
existingEnText.Value = input.MenuTextEn;
await _repositoryText.UpdateAsync(existingEnText);
}
else
{
await _repositoryText.InsertAsync(new LanguageText
{
Key = input.Code,
CultureName = "en",
Value = input.MenuTextEn,
ResourceName = PlatformConsts.AppName
});
}
// Türkçe text oluşturuluyor veya güncelleniyor.
var existingTrText = await _repositoryText.FirstOrDefaultAsync(
a => a.CultureName == "tr" &&
a.Key == input.Code &&
a.ResourceName == PlatformConsts.AppName);
if (existingTrText != null)
{
existingTrText.Value = input.MenuTextTr;
await _repositoryText.UpdateAsync(existingTrText);
}
else
{
await _repositoryText.InsertAsync(new LanguageText
{
Key = input.Code,
CultureName = "tr",
Value = input.MenuTextTr,
ResourceName = PlatformConsts.AppName
});
}
return await base.CreateAsync(input);
}
} }

View file

@ -450,7 +450,7 @@
"code": "Abp.Mailing.DefaultFromDisplayName", "code": "Abp.Mailing.DefaultFromDisplayName",
"nameKey": "Abp.Mailing.DefaultFromDisplayName", "nameKey": "Abp.Mailing.DefaultFromDisplayName",
"descriptionKey": "Abp.Mailing.DefaultFromDisplayName.Description", "descriptionKey": "Abp.Mailing.DefaultFromDisplayName.Description",
"defaultValue": "Sozsoft", "defaultValue": "Sözsoft",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": "T|G|D", "providers": "T|G|D",
"isInherited": false, "isInherited": false,

View file

@ -407,7 +407,7 @@ public static class PlatformConsts
public static class Wizard public static class Wizard
{ {
public static string WizardKey(string code) => $"{Prefix.App}.{AppName}.{code}"; public static string WizardKey(string code) => $"{Prefix.App}.Wizard.{code}";
public static string WizardKeyTitle(string code) => $"{WizardKey(code)}.Title"; public static string WizardKeyTitle(string code) => $"{WizardKey(code)}.Title";
public static string WizardKeyDesc(string code) => $"{WizardKey(code)}.Desc"; public static string WizardKeyDesc(string code) => $"{WizardKey(code)}.Desc";
public static string WizardKeyParent(string code) => $"{WizardKey(code)}.Parent"; public static string WizardKeyParent(string code) => $"{WizardKey(code)}.Parent";
@ -418,12 +418,12 @@ public static class PlatformConsts
public static string PermExport(string code) => $"{WizardKey(code)}.Export"; public static string PermExport(string code) => $"{WizardKey(code)}.Export";
public static string PermImport(string code) => $"{WizardKey(code)}.Import"; public static string PermImport(string code) => $"{WizardKey(code)}.Import";
public static string PermNote(string code) => $"{WizardKey(code)}.Note"; public static string PermNote(string code) => $"{WizardKey(code)}.Note";
public static string LangKeyCreate => $"{Prefix.App}.Create"; public static string LangKeyCreate => "Create";
public static string LangKeyUpdate => $"{Prefix.App}.Update"; public static string LangKeyUpdate => "Update";
public static string LangKeyDelete => $"{Prefix.App}.Delete"; public static string LangKeyDelete => "Delete";
public static string LangKeyExport => $"{Prefix.App}.Export"; public static string LangKeyExport => "Export";
public static string LangKeyImport => $"{Prefix.App}.Import"; public static string LangKeyImport => "Import";
public static string LangKeyNote => $"{Prefix.App}.Note"; public static string LangKeyNote => "Note";
public static string MenuUrl(string code) => $"/admin/list/{code}"; public static string MenuUrl(string code) => $"/admin/list/{code}";
public static string MenuIcon => "FcList"; public static string MenuIcon => "FcList";
@ -494,8 +494,8 @@ public static class PlatformConsts
{ {
return JsonSerializer.Serialize(new[] return JsonSerializer.Serialize(new[]
{ {
new { FieldName = "DeleterId", FieldDbType = DbType.Guid.ToString(), Value = "@USERID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }, new { FieldName = "DeleterId", FieldDbType = DbType.Guid, Value = "@USERID", CustomValueType = FieldCustomValueTypeEnum.CustomKey },
new { FieldName = "Id", FieldDbType = dbType.ToString(), Value = "@ID", CustomValueType = FieldCustomValueTypeEnum.CustomKey } new { FieldName = "Id", FieldDbType = dbType, Value = "@ID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }
}); });
} }

View file

@ -401,7 +401,7 @@ const PublicLayout = () => {
<div className="border-t border-gray-800 mt-12 pt-8"> <div className="border-t border-gray-800 mt-12 pt-8">
<div className="flex flex-col md:flex-row justify-between items-center"> <div className="flex flex-col md:flex-row justify-between items-center">
<p className="text-gray-400 text-sm"> <p className="text-gray-400 text-sm">
&copy; {currentYear} Sozsoft Platform. {translate('::Public.footer.copyright')} &copy; {currentYear} Sözsoft Platform. {translate('::Public.footer.copyright')}
</p> </p>
<div className="mt-4 md:mt-0"> <div className="mt-4 md:mt-0">
<ul className="flex space-x-6 text-sm"> <ul className="flex space-x-6 text-sm">

View file

@ -32,8 +32,11 @@ export interface ListFormWizardColumnGroupDto {
} }
export interface ListFormWizardDto { export interface ListFormWizardDto {
wizardName: string
listFormCode: string listFormCode: string
menuCode: string menuCode: string
isTenant: boolean
isBranch: boolean
languageTextMenuEn: string languageTextMenuEn: string
languageTextMenuTr: string languageTextMenuTr: string
languageTextTitleEn: string languageTextTitleEn: string

View file

@ -17,4 +17,6 @@ export interface MenuDto extends FullAuditedEntityDto<string> {
cultureName?: string cultureName?: string
group?: string group?: string
shortName?: string shortName?: string
menuTextTr?: string
menuTextEn?: string
} }

View file

@ -25,6 +25,16 @@ export class MenuService {
{ apiName: this.apiName }, { apiName: this.apiName },
) )
createWithLanguageKeyText = (input: MenuDto) =>
apiService.fetchData<MenuDto, MenuDto>(
{
method: 'POST',
url: '/api/app/menu/with-language-key-text',
data: input,
},
{ apiName: this.apiName },
)
delete = (id: string) => delete = (id: string) =>
apiService.fetchData<void>( apiService.fetchData<void>(
{ {
@ -51,7 +61,7 @@ export class MenuService {
params: { params: {
sorting: input.sorting, sorting: input.sorting,
skipCount: input.skipCount, skipCount: input.skipCount,
maxResultCount: input.maxResultCount maxResultCount: input.maxResultCount,
}, },
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
@ -80,11 +90,9 @@ export class MenuService {
export const getMenus = async (skipCount = 0, maxResultCount = 1000, sorting = 'order') => { export const getMenus = async (skipCount = 0, maxResultCount = 1000, sorting = 'order') => {
const menuService = new MenuService() const menuService = new MenuService()
return await menuService.getList( return await menuService.getList({
{ sorting,
sorting, skipCount,
skipCount, maxResultCount,
maxResultCount, })
}
)
} }

View file

@ -28,11 +28,15 @@ import WizardStep3, { WizardGroup } from './WizardStep3'
import WizardStep4 from './WizardStep4' import WizardStep4 from './WizardStep4'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { sqlDataTypeToDbType } from './edit/options' import { sqlDataTypeToDbType } from './edit/options'
import { useStoreActions } from '@/store/store'
// ─── Formik initial values & validation ────────────────────────────────────── // ─── Formik initial values & validation ──────────────────────────────────────
const initialValues: ListFormWizardDto = { const initialValues: ListFormWizardDto = {
wizardName: '',
listFormCode: '', listFormCode: '',
menuCode: '', menuCode: '',
isTenant: false,
isBranch: false,
languageTextMenuEn: '', languageTextMenuEn: '',
languageTextMenuTr: '', languageTextMenuTr: '',
languageTextTitleEn: '', languageTextTitleEn: '',
@ -53,6 +57,7 @@ const initialValues: ListFormWizardDto = {
} }
const step1ValidationSchema = Yup.object().shape({ const step1ValidationSchema = Yup.object().shape({
wizardName: Yup.string().required(),
menuCode: Yup.string().required(), menuCode: Yup.string().required(),
permissionGroupName: Yup.string().required(), permissionGroupName: Yup.string().required(),
menuParentCode: Yup.string().required(), menuParentCode: Yup.string().required(),
@ -62,16 +67,18 @@ const step1ValidationSchema = Yup.object().shape({
const step2ValidationSchema = Yup.object().shape({ const step2ValidationSchema = Yup.object().shape({
listFormCode: Yup.string().required(), listFormCode: Yup.string().required(),
languageTextTitleEn: Yup.string(), languageTextTitleEn: Yup.string().required(),
languageTextTitleTr: Yup.string(), languageTextTitleTr: Yup.string().required(),
languageTextDescEn: Yup.string(), languageTextDescEn: Yup.string().required(),
languageTextDescTr: Yup.string(), languageTextDescTr: Yup.string().required(),
dataSourceCode: Yup.string().required(), dataSourceCode: Yup.string().required(),
dataSourceConnectionString: Yup.string(), dataSourceConnectionString: Yup.string(),
selectCommandType: Yup.string().required(), selectCommandType: Yup.string().required(),
selectCommand: Yup.string().required(), selectCommand: Yup.string().required(),
keyFieldName: Yup.string().required(), keyFieldName: Yup.string().required(),
keyFieldDbSourceType: Yup.string().required(), keyFieldDbSourceType: Yup.number().required(),
isTenant: Yup.boolean(),
isBranch: Yup.boolean(),
}) })
const listFormValidationSchema = step1ValidationSchema.concat(step2ValidationSchema) const listFormValidationSchema = step1ValidationSchema.concat(step2ValidationSchema)
@ -80,7 +87,6 @@ const listFormValidationSchema = step1ValidationSchema.concat(step2ValidationSch
const Wizard = () => { const Wizard = () => {
const [currentStep, setCurrentStep] = useState(0) const [currentStep, setCurrentStep] = useState(0)
const [wizardName, setWizardName] = useState('')
// ── Data Source ── // ── Data Source ──
const [isLoadingDataSource, setIsLoadingDataSource] = useState(false) const [isLoadingDataSource, setIsLoadingDataSource] = useState(false)
@ -214,12 +220,12 @@ const Wizard = () => {
// Auto-derive listFormCode from wizardName // Auto-derive listFormCode from wizardName
const deriveListFormCode = (name: string) => { const deriveListFormCode = (name: string) => {
const sanitized = name.replace(/[^a-zA-Z0-9]/g, '') const sanitized = name.replace(/\s/g, '')
return sanitized ? `App.${sanitized}` : '' return sanitized ? `App.Wizard.${sanitized}` : ''
} }
const handleWizardNameChange = (name: string) => { const handleWizardNameChange = (name: string) => {
setWizardName(name) formikRef.current?.setFieldValue('wizardName', name)
const derived = deriveListFormCode(name) const derived = deriveListFormCode(name)
formikRef.current?.setFieldValue('listFormCode', derived) formikRef.current?.setFieldValue('listFormCode', derived)
formikRef.current?.setFieldValue('menuCode', derived) formikRef.current?.setFieldValue('menuCode', derived)
@ -274,11 +280,12 @@ const Wizard = () => {
await formikRef.current.setTouched({ ...formikRef.current.touched, ...touchedStep1 }) await formikRef.current.setTouched({ ...formikRef.current.touched, ...touchedStep1 })
// Also require wizardName // Also require wizardName
if (!wizardName.trim() || hasStep1Errors) return if (hasStep1Errors) return
setCurrentStep(1) setCurrentStep(1)
} }
const handleBack = () => setCurrentStep(0) const handleBack = () => setCurrentStep(0)
const { getConfig } = useStoreActions((a) => a.abpConfig)
const handleNext2 = async () => { const handleNext2 = async () => {
if (!formikRef.current) return if (!formikRef.current) return
@ -322,13 +329,10 @@ const Wizard = () => {
</Notification>, </Notification>,
{ placement: 'top-end' }, { placement: 'top-end' },
) )
getConfig(true)
setTimeout(() => { setTimeout(() => {
navigate( navigate(ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode))
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
':listFormCode',
values.listFormCode,
),
)
}, 1500) }, 1500)
} }
@ -368,14 +372,13 @@ const Wizard = () => {
{ placement: 'top-end' }, { placement: 'top-end' },
) )
setSubmitting(false) setSubmitting(false)
getConfig(true)
setTimeout(() => { setTimeout(() => {
navigate( navigate(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace( ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode),
':listFormCode',
values.listFormCode,
),
) )
}, 500) }, 1500)
} catch (error: any) { } catch (error: any) {
toast.push(<Notification title={error.message} type="danger" />, { toast.push(<Notification title={error.message} type="danger" />, {
placement: 'top-end', placement: 'top-end',
@ -385,14 +388,16 @@ const Wizard = () => {
> >
{({ touched, errors, isSubmitting, values }) => ( {({ touched, errors, isSubmitting, values }) => (
<Form> <Form>
<FormContainer size={currentStep === 2 ? undefined : currentStep === 3 ? undefined : 'sm'}> <FormContainer
size={currentStep === 2 ? undefined : currentStep === 3 ? undefined : 'sm'}
>
{/* ─── Step 1: Basic Info ─────────────────────────────── */} {/* ─── Step 1: Basic Info ─────────────────────────────── */}
{currentStep === 0 && ( {currentStep === 0 && (
<WizardStep1 <WizardStep1
values={values} values={values}
errors={errors} errors={errors}
touched={touched} touched={touched}
wizardName={wizardName} wizardName={values.wizardName}
onWizardNameChange={handleWizardNameChange} onWizardNameChange={handleWizardNameChange}
rawMenuItems={rawMenuItems} rawMenuItems={rawMenuItems}
menuTree={menuTree} menuTree={menuTree}
@ -453,7 +458,7 @@ const Wizard = () => {
{currentStep === 3 && ( {currentStep === 3 && (
<WizardStep4 <WizardStep4
values={values} values={values}
wizardName={wizardName} wizardName={values.wizardName}
selectedColumns={selectedColumns} selectedColumns={selectedColumns}
selectCommandColumns={selectCommandColumns} selectCommandColumns={selectCommandColumns}
groups={editingGroups} groups={editingGroups}

View file

@ -348,7 +348,7 @@ function MenuTreeInline({
<div <div
className={`rounded-lg border ${invalid ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} bg-white dark:bg-gray-800 overflow-hidden`} className={`rounded-lg border ${invalid ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} bg-white dark:bg-gray-800 overflow-hidden`}
> >
<div className="h-72 overflow-y-auto py-1"> <div className="h-96 overflow-y-auto py-1">
{isLoading ? ( {isLoading ? (
<div className="px-4 py-3 text-sm text-gray-400">Loading</div> <div className="px-4 py-3 text-sm text-gray-400">Loading</div>
) : enrichedNodes.length === 0 ? ( ) : enrichedNodes.length === 0 ? (
@ -498,8 +498,10 @@ function MenuAddDialog({
onSaved, onSaved,
}: MenuAddDialogProps) { }: MenuAddDialogProps) {
const [form, setForm] = useState({ const [form, setForm] = useState({
name: '',
code: '', code: '',
displayName: '', menuTextEn: '',
menuTextTr: '',
parentCode: initialParentCode, parentCode: initialParentCode,
icon: '', icon: '',
shortName: '', shortName: '',
@ -510,8 +512,10 @@ function MenuAddDialog({
useEffect(() => { useEffect(() => {
if (isOpen) if (isOpen)
setForm({ setForm({
name: '',
code: '', code: '',
displayName: '', menuTextEn: '',
menuTextTr: '',
parentCode: initialParentCode, parentCode: initialParentCode,
icon: '', icon: '',
shortName: '', shortName: '',
@ -520,18 +524,22 @@ function MenuAddDialog({
}, [isOpen, initialParentCode, initialOrder]) }, [isOpen, initialParentCode, initialOrder])
const handleSave = async () => { const handleSave = async () => {
if (!form.code.trim() || !form.displayName.trim()) return if (!form.code.trim() || !form.menuTextEn.trim()) return
setSaving(true) setSaving(true)
try { try {
await menuService.create({ // Menü oluşturuluyor
await menuService.createWithLanguageKeyText({
code: form.code.trim(), code: form.code.trim(),
displayName: form.displayName.trim(), displayName: form.code.trim(),
parentCode: form.parentCode.trim() || undefined, parentCode: form.parentCode.trim() || undefined,
icon: form.icon || undefined, icon: form.icon || undefined,
shortName: form.shortName.trim() || undefined, shortName: form.shortName.trim() || undefined,
order: form.order, order: form.order,
isDisabled: false, isDisabled: false,
menuTextTr: form.menuTextTr.trim(),
menuTextEn: form.menuTextEn.trim(),
} as MenuDto) } as MenuDto)
onSaved() onSaved()
onClose() onClose()
} catch (e: any) { } catch (e: any) {
@ -544,74 +552,125 @@ function MenuAddDialog({
// suppress unused warning — rawItems kept for future use // suppress unused warning — rawItems kept for future use
void rawItems void rawItems
const fieldCls =
'h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400 w-full'
const disabledCls =
'h-11 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 text-sm text-gray-400 dark:text-gray-500 cursor-not-allowed w-full'
const labelCls = 'text-xs font-medium text-gray-500 dark:text-gray-400 mb-1'
return ( return (
<Dialog isOpen={isOpen} onClose={onClose} onRequestClose={onClose} width={520}> <Dialog isOpen={isOpen} onClose={onClose} onRequestClose={onClose} width={680}>
<div className="flex flex-col gap-4 p-4"> <div className="flex flex-col gap-5 p-5">
<h5 className="text-base font-semibold text-gray-800 dark:text-gray-100">Yeni Menü Ekle</h5> {/* Header */}
<div className="flex flex-col gap-3"> <div className="flex items-center gap-2 pb-1 border-b border-gray-100 dark:border-gray-700">
<div className="flex flex-col gap-1"> <FaPlus className="text-green-500 text-sm" />
<label className="text-sm font-medium text-gray-600 dark:text-gray-300"> <h5 className="text-base font-semibold text-gray-800 dark:text-gray-100">Yeni Menü Ekle</h5>
</div>
{/* Row 1 — Name | Code */}
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col">
<label className={labelCls}>
Name <span className="text-red-500">*</span>
</label>
<input
autoFocus
value={form.name}
onChange={(e) =>
setForm((p) => ({
...p,
name: e.target.value.replace(/\s+/g, ''),
code: `App.Wizard.${e.target.value.replace(/\s+/g, '')}`,
}))
}
placeholder="MyMenu"
className={fieldCls}
/>
</div>
<div className="flex flex-col">
<label className={labelCls}>
Code <span className="text-red-500">*</span> Code <span className="text-red-500">*</span>
</label> </label>
<input <input
value={form.code} value={form.code}
onChange={(e) => setForm((p) => ({ ...p, code: e.target.value }))} disabled
placeholder="App.MyMenu" placeholder="App.Wizard.MyMenu"
className="h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400" className={disabledCls}
/> />
</div> </div>
<div className="flex flex-col gap-1"> </div>
<label className="text-sm font-medium text-gray-600 dark:text-gray-300">
Display Name <span className="text-red-500">*</span> {/* Row 3 — Icon (full width) */}
<div className="flex flex-col">
<label className={labelCls}>
Icon <span className="text-red-500">*</span>
</label>
<IconPickerField
value={form.icon}
onChange={(key) => setForm((p) => ({ ...p, icon: key }))}
/>
</div>
{/* Row 2 — Display Name EN | Display Name TR */}
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col">
<label className={labelCls}>
Display Name (English) <span className="text-red-500">*</span>
</label> </label>
<input <input
value={form.displayName} value={form.menuTextEn}
onChange={(e) => setForm((p) => ({ ...p, displayName: e.target.value }))} onChange={(e) => setForm((p) => ({ ...p, menuTextEn: e.target.value }))}
placeholder="My Menu" placeholder="My Menu"
className="h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400" className={fieldCls}
/> />
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col">
<label className="text-sm font-medium text-gray-600 dark:text-gray-300">Icon</label> <label className={labelCls}>
<IconPickerField Display Name (Turkish) <span className="text-red-500">*</span>
value={form.icon}
onChange={(key) => setForm((p) => ({ ...p, icon: key }))}
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-600 dark:text-gray-300">
Menu Parent
</label> </label>
<input
value={form.menuTextTr}
onChange={(e) => setForm((p) => ({ ...p, menuTextTr: e.target.value }))}
placeholder="Menüm"
className={fieldCls}
/>
</div>
</div>
{/* Row 4 — Menu Parent | Order */}
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col">
<label className={labelCls}>Menu Parent</label>
<input <input
disabled disabled
value={form.parentCode || '(Ana Menü)'} value={form.parentCode || '(Ana Menü)'}
className="h-11 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 text-sm text-gray-400 dark:text-gray-500 cursor-not-allowed" className={disabledCls}
/> />
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col">
<label className="text-sm font-medium text-gray-600 dark:text-gray-300"> <label className={labelCls}>Sıra (Order)</label>
Sıra (Order)
</label>
<input <input
type="number" type="number"
value={form.order} value={form.order}
onChange={(e) => setForm((p) => ({ ...p, order: Number(e.target.value) }))} onChange={(e) => setForm((p) => ({ ...p, order: Number(e.target.value) }))}
className="h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400" className={fieldCls}
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-600 dark:text-gray-300">
Short Name
</label>
<input
value={form.shortName}
onChange={(e) => setForm((p) => ({ ...p, shortName: e.target.value }))}
placeholder="My Menu (short)"
className="h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400"
/> />
</div> </div>
</div> </div>
<div className="flex justify-end gap-2 pt-2">
{/* Row 5 — Short Name (full width) */}
<div className="flex flex-col">
<label className={labelCls}>Short Name</label>
<input
value={form.shortName}
onChange={(e) => setForm((p) => ({ ...p, shortName: e.target.value }))}
placeholder="My Menu (short)"
className={fieldCls}
/>
</div>
{/* Footer */}
<div className="flex justify-end gap-2 pt-1 border-t border-gray-100 dark:border-gray-700">
<Button size="sm" variant="plain" onClick={onClose}> <Button size="sm" variant="plain" onClick={onClose}>
İptal İptal
</Button> </Button>
@ -619,7 +678,12 @@ function MenuAddDialog({
size="sm" size="sm"
variant="solid" variant="solid"
loading={saving} loading={saving}
disabled={!form.code.trim() || !form.displayName.trim()} disabled={
!form.code.trim() ||
!form.menuTextEn.trim() ||
!form.menuTextTr.trim() ||
!form.icon.trim()
}
onClick={handleSave} onClick={handleSave}
> >
Kaydet Kaydet
@ -698,7 +762,7 @@ const WizardStep1 = ({
placeholder="e.g. Routes, Products, Orders" placeholder="e.g. Routes, Products, Orders"
value={wizardName} value={wizardName}
autoFocus autoFocus
onChange={(e) => onWizardNameChange(e.target.value)} onChange={(e) => onWizardNameChange(e.target.value.replace(/\s/g, ''))}
/> />
</FormItem> </FormItem>
@ -785,14 +849,33 @@ const WizardStep1 = ({
type="text" type="text"
autoComplete="off" autoComplete="off"
name="menuCode" name="menuCode"
placeholder="e.g. App.Routes, App.Products, App.Orders" placeholder="e.g. App.Wizard.Routes, App.Wizard.Products, App.Wizard.Orders"
component={Input} component={Input}
disabled={true}
/> />
</FormItem> </FormItem>
{/* Menu Icon */}
<FormItem
label="Menu Icon"
asterisk={true}
invalid={!!(errors.menuIcon && touched.menuIcon)}
errorMessage={errors.menuIcon}
>
<Field name="menuIcon">
{({ field, form }: FieldProps<string>) => (
<IconPickerField
value={field.value}
onChange={(key) => form.setFieldValue(field.name, key)}
invalid={!!(errors.menuIcon && touched.menuIcon)}
/>
)}
</Field>
</FormItem>
{/* Menu Text (En) */} {/* Menu Text (En) */}
<FormItem <FormItem
label="Menu Text (En)" label="Display Name (English)"
asterisk={true} asterisk={true}
invalid={!!(errors.languageTextMenuEn && touched.languageTextMenuEn)} invalid={!!(errors.languageTextMenuEn && touched.languageTextMenuEn)}
errorMessage={errors.languageTextMenuEn} errorMessage={errors.languageTextMenuEn}
@ -808,7 +891,7 @@ const WizardStep1 = ({
{/* Menu Text (Tr) */} {/* Menu Text (Tr) */}
<FormItem <FormItem
label="Menu Text (Tr)" label="Display Name (Turkish)"
asterisk={true} asterisk={true}
invalid={!!(errors.languageTextMenuTr && touched.languageTextMenuTr)} invalid={!!(errors.languageTextMenuTr && touched.languageTextMenuTr)}
errorMessage={errors.languageTextMenuTr} errorMessage={errors.languageTextMenuTr}
@ -867,7 +950,13 @@ const WizardStep1 = ({
</span> </span>
)} )}
</div> </div>
<Button variant="solid" type="button" icon={<FaArrowRight />} disabled={!step1CanGo} onClick={onNext}> <Button
variant="solid"
type="button"
icon={<FaArrowRight />}
disabled={!step1CanGo}
onClick={onNext}
>
{translate('::Next') || 'Next'} {translate('::Next') || 'Next'}
</Button> </Button>
</div> </div>

View file

@ -1,4 +1,4 @@
import { Button, FormItem, Input, Select } from '@/components/ui' import { Button, Checkbox, FormItem, Input, Select } from '@/components/ui'
import { ListFormWizardDto } from '@/proxy/admin/list-form/models' import { ListFormWizardDto } from '@/proxy/admin/list-form/models'
import { SelectCommandTypeEnum } from '@/proxy/form/models' import { SelectCommandTypeEnum } from '@/proxy/form/models'
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models' import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
@ -66,6 +66,10 @@ const WizardStep2 = ({
!values.dataSourceCode && 'Veri Kaynağı', !values.dataSourceCode && 'Veri Kaynağı',
!values.selectCommand && 'Select Command', !values.selectCommand && 'Select Command',
!values.keyFieldName && 'Key Field', !values.keyFieldName && 'Key Field',
!values.languageTextDescEn && 'Description Text (En)',
!values.languageTextDescTr && 'Description Text (Tr)',
!values.languageTextTitleEn && 'Title Text (En)',
!values.languageTextTitleTr && 'Title Text (Tr)',
selectedColumns.size === 0 && 'Sütun seçimi', selectedColumns.size === 0 && 'Sütun seçimi',
].filter(Boolean) as string[] ].filter(Boolean) as string[]
const step2CanGo = step2Missing.length === 0 const step2CanGo = step2Missing.length === 0
@ -89,8 +93,9 @@ const WizardStep2 = ({
type="text" type="text"
autoComplete="off" autoComplete="off"
name="listFormCode" name="listFormCode"
placeholder="e.g. App.Soutes" placeholder="e.g. App.Wizard.Routes"
component={Input} component={Input}
disabled={true}
/> />
</FormItem> </FormItem>
@ -145,66 +150,10 @@ const WizardStep2 = ({
/> />
</FormItem> </FormItem>
)} )}
<FormItem
label="Title Text (En)"
invalid={!!(errors.languageTextTitleEn && touched.languageTextTitleEn)}
errorMessage={errors.languageTextTitleEn}
>
<Field
type="text"
autoComplete="off"
name="languageTextTitleEn"
placeholder="English Title Text"
component={Input}
/>
</FormItem>
<FormItem
label="Title Text (Tr)"
invalid={!!(errors.languageTextTitleTr && touched.languageTextTitleTr)}
errorMessage={errors.languageTextTitleTr}
>
<Field
type="text"
autoComplete="off"
name="languageTextTitleTr"
placeholder="Turkish Title Text"
component={Input}
/>
</FormItem>
<FormItem
label="Description Text (En)"
invalid={!!(errors.languageTextDescEn && touched.languageTextDescEn)}
errorMessage={errors.languageTextDescEn}
>
<Field
type="text"
autoComplete="off"
name="languageTextDescEn"
placeholder="English Description Text"
component={Input}
/>
</FormItem>
<FormItem
label="Description Text (Tr)"
invalid={!!(errors.languageTextDescTr && touched.languageTextDescTr)}
errorMessage={errors.languageTextDescTr}
>
<Field
type="text"
autoComplete="off"
name="languageTextDescTr"
placeholder="Turkish Description Text"
component={Input}
/>
</FormItem>
</div> </div>
{/* Select Command + Key Field Name */} {/* Select Command + Key Field Name */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<FormItem <FormItem
label="Select Command" label="Select Command"
invalid={!!(errors.selectCommand && touched.selectCommand)} invalid={!!(errors.selectCommand && touched.selectCommand)}
@ -268,7 +217,6 @@ const WizardStep2 = ({
: [] : []
return ( return (
<Select <Select
componentAs={CreatableSelect}
field={field} field={field}
form={form} form={form}
isClearable isClearable
@ -361,6 +309,94 @@ const WizardStep2 = ({
)} )}
</Field> </Field>
</FormItem> </FormItem>
<FormItem
label="Is Tenant?"
invalid={!!(errors.isTenant && touched.isTenant)}
errorMessage={errors.isTenant}
>
<Field
type="checkbox"
autoComplete="off"
name="isTenant"
component={Checkbox}
/>
</FormItem>
<FormItem
label="Is Branch?"
invalid={!!(errors.isBranch && touched.isBranch)}
errorMessage={errors.isBranch}
>
<Field
type="checkbox"
autoComplete="off"
name="isBranch"
component={Checkbox}
/>
</FormItem>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<FormItem
label="Title Text (English)"
asterisk={true}
invalid={!!(errors.languageTextTitleEn && touched.languageTextTitleEn)}
errorMessage={errors.languageTextTitleEn}
>
<Field
type="text"
autoComplete="off"
name="languageTextTitleEn"
placeholder="English Title Text"
component={Input}
/>
</FormItem>
<FormItem
label="Title Text (Turkish)"
asterisk={true}
invalid={!!(errors.languageTextTitleTr && touched.languageTextTitleTr)}
errorMessage={errors.languageTextTitleTr}
>
<Field
type="text"
autoComplete="off"
name="languageTextTitleTr"
placeholder="Turkish Title Text"
component={Input}
/>
</FormItem>
<FormItem
label="Description Text (English)"
asterisk={true}
invalid={!!(errors.languageTextDescEn && touched.languageTextDescEn)}
errorMessage={errors.languageTextDescEn}
>
<Field
type="text"
autoComplete="off"
name="languageTextDescEn"
placeholder="English Description Text"
component={Input}
/>
</FormItem>
<FormItem
label="Description Text (Turkish)"
asterisk={true}
invalid={!!(errors.languageTextDescTr && touched.languageTextDescTr)}
errorMessage={errors.languageTextDescTr}
>
<Field
type="text"
autoComplete="off"
name="languageTextDescTr"
placeholder="Turkish Description Text"
component={Input}
/>
</FormItem>
</div> </div>
{/* Column Selection Panel */} {/* Column Selection Panel */}

View file

@ -751,17 +751,17 @@ const WizardStep3 = ({
{/* ── Fixed Footer ─────────────────────────────────────────────────── */} {/* ── Fixed Footer ─────────────────────────────────────────────────── */}
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center"> <div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
<div className="flex items-center gap-3 w-full"> <div className="flex items-center gap-3 w-full">
<Button variant="default" type="button" icon={<FaCode />} onClick={() => setIsHelperOpen(true)}> <Button size='sm' variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack}>
{translate('::Helper Codes') || 'Helper Codes'}
</Button>
<Button variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack}>
{translate('::Back') || 'Back'} {translate('::Back') || 'Back'}
</Button> </Button>
<Button size='sm' variant="default" type="button" icon={<FaCode />} onClick={() => setIsHelperOpen(true)}>
{translate('::Helper Codes') || 'Helper Codes'}
</Button>
<div className="flex-1 flex items-center justify-end gap-3"> <div className="flex-1 flex items-center justify-end gap-3">
{!canProceed && ( {!canProceed && (
<span className="text-xs text-amber-600 dark:text-amber-400 font-medium"> {validationMsg}</span> <span className="text-xs text-amber-600 dark:text-amber-400 font-medium"> {validationMsg}</span>
)} )}
<Button variant="solid" type="button" icon={<FaArrowRight />} disabled={!canProceed} onClick={onNext}> <Button size='sm' variant="solid" type="button" icon={<FaArrowRight />} disabled={!canProceed} onClick={onNext}>
{translate('::Next') || 'Next'} {translate('::Next') || 'Next'}
</Button> </Button>
</div> </div>

View file

@ -13,7 +13,7 @@ import {
FaRocket, FaRocket,
FaSpinner, FaSpinner,
} from 'react-icons/fa' } from 'react-icons/fa'
import { selectCommandTypeOptions } from './edit/options' import { dbSourceTypeOptions, selectCommandTypeOptions } from './edit/options'
// ─── Types ──────────────────────────────────────────────────────────────────── // ─── Types ────────────────────────────────────────────────────────────────────
@ -230,7 +230,13 @@ const WizardStep4 = ({
/> />
<Row label="Select Command" value={values.selectCommand} /> <Row label="Select Command" value={values.selectCommand} />
<Row label="Key Field" value={values.keyFieldName} /> <Row label="Key Field" value={values.keyFieldName} />
<Row label="Key Field Tipi" value={String(values.keyFieldDbSourceType)} /> <Row
label="Key Field Tipi"
value={
dbSourceTypeOptions.find((o: any) => o.value === values.keyFieldDbSourceType)
?.label ?? String(values.keyFieldDbSourceType)
}
/>
</Section> </Section>
</div> </div>
@ -384,11 +390,19 @@ const WizardStep4 = ({
{/* ── Fixed Footer ─────────────────────────────────────────────── */} {/* ── Fixed Footer ─────────────────────────────────────────────── */}
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center"> <div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
<div className="flex items-center gap-3 w-full"> <div className="flex items-center gap-3 w-full">
<Button variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack} disabled={isDeploying}> <Button
size='sm'
variant="default"
type="button"
icon={<FaArrowLeft />}
onClick={onBack}
disabled={isDeploying}
>
{translate('::Back') || 'Back'} {translate('::Back') || 'Back'}
</Button> </Button>
<div className="flex-1 flex items-center justify-end"> <div className="flex-1 flex items-center justify-end">
<Button <Button
size='sm'
variant="solid" variant="solid"
type="button" type="button"
icon={<FaRocket />} icon={<FaRocket />}

View file

@ -86,8 +86,8 @@ export default defineConfig(async ({ mode }) => {
: undefined, : undefined,
manifest: { manifest: {
name: 'Sozsoft Platform', name: 'Sözsoft Platform',
short_name: 'Sozsoft Platform', short_name: 'Sözsoft Platform',
theme_color: '#FF99C8', theme_color: '#FF99C8',
background_color: '#f0e7db', background_color: '#f0e7db',
display: 'standalone', display: 'standalone',
@ -112,7 +112,7 @@ export default defineConfig(async ({ mode }) => {
}, },
], ],
categories: ['business', 'productivity'], categories: ['business', 'productivity'],
description: 'Sozsoft Platform Application', description: 'Sözsoft Platform Application',
}, },
}), }),
], ],