Compare commits
33 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
233c9b7502 | ||
|
|
12f046f262 | ||
|
|
d0cccde53f | ||
|
|
1d15c44a3d | ||
|
|
bade0bab98 | ||
|
|
c204eef755 | ||
|
|
27e65f05f0 | ||
|
|
64084679e8 | ||
|
|
2f1b9d4e77 | ||
|
|
1c472a7d9a | ||
|
|
119c3650f0 | ||
|
|
ebab6ea114 | ||
|
|
975bc8dd6c | ||
|
|
97a2a4b38d | ||
|
|
20e7fae481 | ||
|
|
6a5881960f | ||
|
|
37d3065ed7 | ||
|
|
648a63d618 | ||
|
|
197c4e0741 | ||
|
|
921c3c6d35 | ||
|
|
f56eccee55 | ||
|
|
fd5364ff97 | ||
|
|
a9e1a15183 | ||
|
|
d161e0f4b9 | ||
|
|
f9c5910813 | ||
|
|
67286232da | ||
|
|
9875ba3041 | ||
|
|
daf0d51960 | ||
|
|
5e6d2f518b | ||
|
|
2df3e359c4 | ||
|
|
57dbb0d308 | ||
|
|
5806ff5f9f | ||
|
|
e1c808310d |
140 changed files with 7291 additions and 3251 deletions
|
|
@ -17,9 +17,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
<PrivateAssets>All</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ public class MailTrackingManager : DomainService
|
|||
/// <returns></returns>
|
||||
public async Task StartAsync()
|
||||
{
|
||||
// https://us-east-1.console.aws.amazon.com/iam/home?region=eu-central-1#/users
|
||||
// https://eu-central-1.console.aws.amazon.com/ses/home?region=eu-central-1#/identities/system%40sozsoft.com?tabId=authentication
|
||||
var accessKey = configuration.GetValue<string>(AmazonSesEmailSettingNames.AccessKey);
|
||||
var accessKeyId = configuration.GetValue<string>(AmazonSesEmailSettingNames.AccessKeyId);
|
||||
var region = configuration.GetValue<string>(AmazonSesEmailSettingNames.Region);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@
|
|||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.TextTemplating.Razor" Version="10.0.0" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
<PrivateAssets>All</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public class AmazonSesEmailSender : EmailSenderBase, ISozsoftEmailSender, ITrans
|
|||
await BackgroundJobManager.EnqueueAsync(
|
||||
new ErpBackgroundEmailSendingJobArgs
|
||||
{
|
||||
TenantId = CurrentTenant.Id,
|
||||
To = to,
|
||||
Sender = sender,
|
||||
Params = @params,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Volo.Abp.BackgroundJobs;
|
||||
using Volo.Abp.BackgroundJobs;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
|
||||
namespace Sozsoft.Sender.Mail;
|
||||
|
||||
|
|
@ -7,22 +8,28 @@ public class ErpBackgroundEmailSendingJob :
|
|||
AsyncBackgroundJob<ErpBackgroundEmailSendingJobArgs>, ITransientDependency
|
||||
{
|
||||
protected ISozsoftEmailSender EmailSender { get; }
|
||||
protected ICurrentTenant CurrentTenant { get; }
|
||||
|
||||
public ErpBackgroundEmailSendingJob(ISozsoftEmailSender emailSender)
|
||||
public ErpBackgroundEmailSendingJob(
|
||||
ISozsoftEmailSender emailSender,
|
||||
ICurrentTenant currentTenant)
|
||||
{
|
||||
EmailSender = emailSender;
|
||||
CurrentTenant = currentTenant;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(ErpBackgroundEmailSendingJobArgs args)
|
||||
{
|
||||
//await EmailSender.SendEmailAsync(args.Template, args.To, args.Params, args.Subject);
|
||||
await EmailSender.SendEmailAsync(
|
||||
args.To,
|
||||
args.Sender,
|
||||
args.Params,
|
||||
args.TextContent,
|
||||
args.Subject,
|
||||
args.Attachments);
|
||||
using (CurrentTenant.Change(args.TenantId))
|
||||
{
|
||||
//await EmailSender.SendEmailAsync(args.Template, args.To, args.Params, args.Subject);
|
||||
await EmailSender.SendEmailAsync(
|
||||
args.To,
|
||||
args.Sender,
|
||||
args.Params,
|
||||
args.TextContent,
|
||||
args.Subject,
|
||||
args.Attachments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
namespace Sozsoft.Sender.Mail;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
|
||||
namespace Sozsoft.Sender.Mail;
|
||||
|
||||
[Serializable]
|
||||
public class ErpBackgroundEmailSendingJobArgs
|
||||
public class ErpBackgroundEmailSendingJobArgs : IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string[] To { get; set; }
|
||||
|
||||
public KeyValuePair<string, string>? Sender { get; set; }
|
||||
|
|
@ -14,6 +18,4 @@ public class ErpBackgroundEmailSendingJobArgs
|
|||
public Dictionary<string, string>? Attachments { get; set; }
|
||||
|
||||
public string? TextContent { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
<PackageReference Include="Volo.Abp.MailKit" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.Sms" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ using System.Threading.Tasks;
|
|||
using Sozsoft.Languages;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.SettingManagement;
|
||||
using Volo.Abp.Settings;
|
||||
using SettingDefinition = Sozsoft.Settings.Entities.SettingDefinition;
|
||||
|
||||
namespace Sozsoft.Settings;
|
||||
|
||||
|
|
@ -20,17 +19,20 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
|||
private readonly ISettingDefinitionManager settingDefinitionManager;
|
||||
private readonly ISettingManager settingManager;
|
||||
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
|
||||
private readonly ICurrentTenant currentTenant;
|
||||
|
||||
public SettingUiAppService(
|
||||
ILanguageKeyIntegrationService languageKeyIntegrationService,
|
||||
ISettingDefinitionManager settingDefinitionManager,
|
||||
ISettingManager settingManager,
|
||||
ErpSettingDefinitionManager ErpSettingDefinitionManager)
|
||||
ErpSettingDefinitionManager ErpSettingDefinitionManager,
|
||||
ICurrentTenant currentTenant)
|
||||
{
|
||||
this.languageKeyIntegrationService = languageKeyIntegrationService;
|
||||
this.settingDefinitionManager = settingDefinitionManager;
|
||||
this.settingManager = settingManager;
|
||||
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
|
||||
this.currentTenant = currentTenant;
|
||||
}
|
||||
|
||||
public virtual async Task<List<MainGroupedSettingDto>> GetListAsync()
|
||||
|
|
@ -95,15 +97,18 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
|||
{
|
||||
if (setting.Providers.IsNullOrEmpty())
|
||||
{
|
||||
await settingManager.SetForCurrentUserAsync(setting.Name, value);
|
||||
if (currentTenant.Id.HasValue)
|
||||
{
|
||||
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
await settingManager.SetGlobalAsync(setting.Name, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setting.Providers.Any(p => p == UserSettingValueProvider.ProviderName))
|
||||
{
|
||||
await settingManager.SetForCurrentUserAsync(setting.Name, value);
|
||||
}
|
||||
else if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName))
|
||||
if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName) && currentTenant.Id.HasValue)
|
||||
{
|
||||
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
|
||||
}
|
||||
|
|
@ -111,6 +116,10 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
|||
{
|
||||
await settingManager.SetGlobalAsync(setting.Name, value);
|
||||
}
|
||||
else if (setting.Providers.Any(p => p == UserSettingValueProvider.ProviderName))
|
||||
{
|
||||
await settingManager.SetForCurrentUserAsync(setting.Name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,22 +28,38 @@ public class SettingsDefinitionProvider : SettingDefinitionProvider
|
|||
foreach (var item in settingDefinitions.OrderBy(a => a.Order))
|
||||
{
|
||||
var iDescription = item.DescriptionKey.IsNullOrEmpty() ? null : L(item.DescriptionKey);
|
||||
var providers = item.Providers.IsNullOrEmpty()
|
||||
? []
|
||||
: item.Providers.Split(MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var def = new Volo.Abp.Settings.SettingDefinition(
|
||||
item.Code, item.DefaultValue, L(item.NameKey), iDescription,
|
||||
item.IsVisibleToClients, item.IsInherited, item.IsEncrypted)
|
||||
.WithProperty(SettingsConsts.MainGroup, item.MainGroupKey)
|
||||
var def = context.GetOrNull(item.Code);
|
||||
|
||||
if (def == null)
|
||||
{
|
||||
def = new Volo.Abp.Settings.SettingDefinition(
|
||||
item.Code, item.DefaultValue, L(item.NameKey), iDescription,
|
||||
item.IsVisibleToClients, item.IsInherited, item.IsEncrypted);
|
||||
|
||||
context.Add(def);
|
||||
}
|
||||
else
|
||||
{
|
||||
def.DefaultValue = item.DefaultValue;
|
||||
def.DisplayName = L(item.NameKey);
|
||||
def.Description = iDescription;
|
||||
def.IsVisibleToClients = item.IsVisibleToClients;
|
||||
def.IsInherited = item.IsInherited;
|
||||
def.IsEncrypted = item.IsEncrypted;
|
||||
def.Properties.Clear();
|
||||
def.Providers.Clear();
|
||||
}
|
||||
|
||||
def.Providers.AddRange(providers);
|
||||
def.WithProperty(SettingsConsts.MainGroup, item.MainGroupKey)
|
||||
.WithProperty(SettingsConsts.SubGroup, item.SubGroupKey)
|
||||
.WithProperty(SettingsConsts.DataType, item.DataType)
|
||||
.WithProperty(SettingsConsts.RequiredPermission, item.RequiredPermissionName)
|
||||
.WithProperty(SettingsConsts.SelectOptions, item.SelectOptions?.ToDictionary(x => x.Key, x => L(x.Value)));
|
||||
|
||||
if (!item.Providers.IsNullOrEmpty())
|
||||
{
|
||||
def.Providers.AddRange(item.Providers.Split(MultiValueDelimiter));
|
||||
}
|
||||
|
||||
context.Add(def);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Volo.Abp.Modularity;
|
||||
using Volo.Abp.Modularity;
|
||||
using Volo.Abp.SettingManagement;
|
||||
using Volo.Abp.Settings;
|
||||
|
||||
namespace Sozsoft.Settings;
|
||||
|
||||
|
|
@ -11,7 +12,10 @@ public class SettingsDomainModule : AbpModule
|
|||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
|
||||
Configure<AbpSettingOptions>(options =>
|
||||
{
|
||||
options.DefinitionProviders.Remove<SettingsDefinitionProvider>();
|
||||
options.DefinitionProviders.Add<SettingsDefinitionProvider>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
<PrivateAssets>All</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
<PrivateAssets>All</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
<PackageReference Include="Scriban" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.AuditLogs;
|
||||
|
||||
public class AuditLogListRequestDto : PagedAndSortedResultRequestDto
|
||||
{
|
||||
public string ListFormCode { get; set; }
|
||||
public string EntityId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using Volo.Abp.Content;
|
||||
|
||||
namespace Sozsoft.Platform.Identity.Dto;
|
||||
|
||||
public class UserAvatarUpdateInput
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
public IRemoteStreamContent Avatar { get; set; }
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace Sozsoft.Platform.ListForms;
|
|||
public class WorkflowDto
|
||||
{
|
||||
public string ApprovalUserFieldName { get; set; }
|
||||
public bool IsFilterUserName { get; set; }
|
||||
public string ApprovalDateFieldName { get; set; }
|
||||
public string ApprovalStatusFieldName { get; set; }
|
||||
public string ApprovalDescriptionFieldName { get; set; }
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public class ListFormWizardDto
|
|||
|
||||
public string PermissionGroupName { get; set; }
|
||||
public string MenuParentCode { get; set; }
|
||||
public string MenuParentIcon { get; set; }
|
||||
public string MenuIcon { get; set; }
|
||||
public string DataSourceCode { get; set; }
|
||||
public string DataSourceConnectionString { get; set; }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class WizardColumnItemInputDto
|
|||
public string EditorScript { get; set; }
|
||||
public int ColSpan { get; set; } = 1;
|
||||
public bool IsRequired { get; set; }
|
||||
public bool IncludeInEditingForm { get; set; } = true;
|
||||
public DbType DbSourceType { get; set; } = DbType.String;
|
||||
public string TurkishCaption { get; set; }
|
||||
public string EnglishCaption { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Sozsoft.Platform.ListForms;
|
||||
|
||||
public class WorkflowRunResultDto
|
||||
|
|
@ -8,5 +10,6 @@ public class WorkflowRunResultDto
|
|||
public string CurrentNodeKind { get; set; }
|
||||
public bool WaitingApproval { get; set; }
|
||||
public bool Completed { get; set; }
|
||||
public List<string> ToastMessages { get; set; } = [];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ public class RouteDto : EntityDto<Guid>
|
|||
{
|
||||
public string Key { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string ComponentType { get; set; }
|
||||
public string ComponentPath { get; set; }
|
||||
public string RouteType { get; set; }
|
||||
public string[] Authority { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,30 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.AuditLogging;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Uow;
|
||||
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
|
||||
|
||||
namespace Sozsoft.Platform.AuditLogs;
|
||||
|
||||
public interface IAuditLogAppService
|
||||
: ICrudAppService<AuditLogDto, Guid>
|
||||
: ICrudAppService<AuditLogDto, Guid, AuditLogListRequestDto>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Authorize(AppCodes.IdentityManagement.AuditLogs)]
|
||||
public class AuditLogAppService
|
||||
: CrudAppService<AuditLog, AuditLogDto, Guid>
|
||||
, IAuditLogAppService
|
||||
public class AuditLogAppService : CrudAppService<
|
||||
AuditLog,
|
||||
AuditLogDto,
|
||||
Guid,
|
||||
AuditLogListRequestDto>, IAuditLogAppService
|
||||
{
|
||||
public AuditLogAppService(IAuditLogRepository auditLogRepository) : base(auditLogRepository)
|
||||
private readonly IRepository<ListForm, Guid> _listFormRepository;
|
||||
|
||||
public AuditLogAppService(
|
||||
IAuditLogRepository auditLogRepository,
|
||||
IRepository<ListForm, Guid> listFormRepository
|
||||
) : base(auditLogRepository)
|
||||
{
|
||||
_listFormRepository = listFormRepository;
|
||||
}
|
||||
|
||||
public override async Task<AuditLogDto> GetAsync(Guid id)
|
||||
|
|
@ -35,27 +48,30 @@ public class AuditLogAppService
|
|||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(PagedAndSortedResultRequestDto input)
|
||||
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogListRequestDto input)
|
||||
{
|
||||
var query = await CreateFilteredQueryAsync(input);
|
||||
var query = await Repository.WithDetailsAsync();
|
||||
|
||||
if (!input.ListFormCode.IsNullOrWhiteSpace())
|
||||
{
|
||||
var filterRules = await GetListFormFilterRulesAsync(input.ListFormCode);
|
||||
query = ApplyAuditLogActionParametersFilter(query, filterRules, input.EntityId);
|
||||
}
|
||||
else if (!input.EntityId.IsNullOrWhiteSpace())
|
||||
{
|
||||
query = query.Where(a => a.Actions.Any(action => action.Parameters.Contains(input.EntityId)));
|
||||
}
|
||||
|
||||
var totalCount = await AsyncExecuter.CountAsync(query);
|
||||
|
||||
query = ApplySorting(query, input);
|
||||
query = ApplyPaging(query, input);
|
||||
|
||||
// EntityChanges ile birlikte getir (N+1 query önlenir)
|
||||
var auditLogRepository = (IAuditLogRepository)Repository;
|
||||
var auditLogsWithDetails = await auditLogRepository.GetListAsync(
|
||||
sorting: input.Sorting,
|
||||
maxResultCount: input.MaxResultCount,
|
||||
skipCount: input.SkipCount,
|
||||
includeDetails: true
|
||||
);
|
||||
var auditLogsWithDetails = await AsyncExecuter.ToListAsync(query);
|
||||
|
||||
// Mapping tek seferde yap
|
||||
var entityDtos = ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogsWithDetails);
|
||||
|
||||
|
||||
// EntityChangeCount'u doldur (artık EntityChanges yüklü)
|
||||
foreach (var dto in entityDtos)
|
||||
{
|
||||
|
|
@ -69,6 +85,102 @@ public class AuditLogAppService
|
|||
);
|
||||
}
|
||||
|
||||
private async Task<List<AuditLogListFormFilterRule>> GetListFormFilterRulesAsync(string listFormCode)
|
||||
{
|
||||
var rules = new List<AuditLogListFormFilterRule>
|
||||
{
|
||||
new(listFormCode, null)
|
||||
};
|
||||
|
||||
var listForm = await _listFormRepository.FirstOrDefaultAsync(a => a.ListFormCode == listFormCode);
|
||||
if (listForm?.SubFormsJson.IsNullOrWhiteSpace() != false)
|
||||
{
|
||||
return rules;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var subForms = JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson) ?? [];
|
||||
foreach (var subForm in subForms.Where(a => !a.Code.IsNullOrWhiteSpace()))
|
||||
{
|
||||
var childFieldNames = subForm.Relation?
|
||||
.Select(a => a.ChildFieldName)
|
||||
.Where(a => !a.IsNullOrWhiteSpace())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList() ?? [];
|
||||
|
||||
rules.AddRange(childFieldNames.Select(childFieldName => new AuditLogListFormFilterRule(subForm.Code, childFieldName)));
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Invalid subform JSON should not block audit log listing for the main form.
|
||||
}
|
||||
|
||||
return rules
|
||||
.DistinctBy(a => $"{a.ListFormCode}|{a.ChildFieldName}".ToLowerInvariant())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static IQueryable<AuditLog> ApplyAuditLogActionParametersFilter(
|
||||
IQueryable<AuditLog> query,
|
||||
List<AuditLogListFormFilterRule> rules,
|
||||
string entityId)
|
||||
{
|
||||
var validRules = rules
|
||||
.Where(rule => !rule.ListFormCode.IsNullOrWhiteSpace())
|
||||
.ToList();
|
||||
|
||||
if (validRules.Count == 0)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
var auditLog = Expression.Parameter(typeof(AuditLog), "auditLog");
|
||||
var action = Expression.Parameter(typeof(AuditLogAction), "action");
|
||||
var parameters = Expression.Property(action, nameof(AuditLogAction.Parameters));
|
||||
|
||||
Expression actionBody = Expression.Constant(false);
|
||||
foreach (var rule in validRules)
|
||||
{
|
||||
Expression ruleBody = Contains(parameters, rule.ListFormCode);
|
||||
if (!entityId.IsNullOrWhiteSpace())
|
||||
{
|
||||
ruleBody = Expression.AndAlso(ruleBody, Contains(parameters, entityId));
|
||||
}
|
||||
|
||||
if (!rule.ChildFieldName.IsNullOrWhiteSpace())
|
||||
{
|
||||
ruleBody = Expression.AndAlso(ruleBody, Contains(parameters, rule.ChildFieldName));
|
||||
}
|
||||
|
||||
actionBody = Expression.OrElse(actionBody, ruleBody);
|
||||
}
|
||||
|
||||
var actions = Expression.Property(auditLog, nameof(AuditLog.Actions));
|
||||
var actionPredicate = Expression.Lambda<Func<AuditLogAction, bool>>(actionBody, action);
|
||||
var anyCall = Expression.Call(
|
||||
typeof(Enumerable),
|
||||
nameof(Enumerable.Any),
|
||||
[typeof(AuditLogAction)],
|
||||
actions,
|
||||
actionPredicate);
|
||||
|
||||
var auditLogPredicate = Expression.Lambda<Func<AuditLog, bool>>(anyCall, auditLog);
|
||||
return query.Where(auditLogPredicate);
|
||||
}
|
||||
|
||||
private static MethodCallExpression Contains(MemberExpression source, string value)
|
||||
{
|
||||
return Expression.Call(
|
||||
source,
|
||||
nameof(string.Contains),
|
||||
Type.EmptyTypes,
|
||||
Expression.Constant(value));
|
||||
}
|
||||
|
||||
private sealed record AuditLogListFormFilterRule(string ListFormCode, string? ChildFieldName);
|
||||
|
||||
// Audit Log kayitlarini gormek istiyoruz fakat degistirmek istemiyoruz
|
||||
[RemoteService(IsEnabled = false)]
|
||||
public override Task<AuditLogDto> CreateAsync(AuditLogDto input)
|
||||
|
|
|
|||
|
|
@ -76,8 +76,7 @@ public class PlatformAccountAppService : AccountAppService, IAccountAppService
|
|||
var SenderName = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromDisplayName);
|
||||
var SenderEmailAddress = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromAddress);
|
||||
|
||||
var url = await appUrlProvider.GetUrlAsync("MVC", PlatformConsts.Urls.UserDetail);
|
||||
var userDetailUrl = $"{url}/{user.Id}";
|
||||
var userDetailUrl = await GetUserDetailUrlAsync(user.Id);
|
||||
|
||||
var content = $@"My name is: {user.GetFullName()}.
|
||||
Email Address: {user.Email}
|
||||
|
|
@ -111,17 +110,16 @@ User Detail: {userDetailUrl}";
|
|||
}
|
||||
|
||||
[Captcha]
|
||||
public async Task SendExtendLoginRequestAsync(SendExtendLoginRequestInputDto input)
|
||||
public async Task<bool> SendExtendLoginRequestAsync(SendExtendLoginRequestInputDto input)
|
||||
{
|
||||
var user = await UserManager.FindByEmailAsync(input.EmailAddress);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var userDetailUrl = await appUrlProvider.GetUrlAsync(PlatformConsts.React, PlatformConsts.Urls.UserDetail);
|
||||
var content = $@"My name is: {user.GetFullName()}.
|
||||
User Detail: {string.Format(userDetailUrl, user.Id)}";
|
||||
User Detail: {await GetUserDetailUrlAsync(user.Id)}";
|
||||
|
||||
var recipient = await settingProvider.GetOrNullAsync(PlatformConsts.AbpSettings.SiteManagement.General.TimedLoginEmails);
|
||||
if (!recipient.IsNullOrWhiteSpace())
|
||||
|
|
@ -132,17 +130,23 @@ User Detail: {string.Format(userDetailUrl, user.Id)}";
|
|||
null,
|
||||
content,
|
||||
subject: PlatformConsts.AppName + " : Extend Login Request");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
[Captcha]
|
||||
public async Task SendAccountConfirmationCodeAsync(SendAccountConfirmationCodeInputDto input)
|
||||
public async Task<bool> SendAccountConfirmationCodeAsync(SendAccountConfirmationCodeInputDto input)
|
||||
{
|
||||
var user = await UserManager.FindByEmailAsync(input.EmailAddress);
|
||||
if (user != null)
|
||||
await SendConfirmationCodeAsync(user);
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await SendConfirmationCodeAsync(user);
|
||||
}
|
||||
|
||||
public async Task<bool> VerifyAccountConfirmationCodeAsync(VerifyAccountConfirmationCodeInputDto input)
|
||||
|
|
@ -203,6 +207,15 @@ To validate your account, please complete your profile by clicking (or copy-past
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<string> GetUserDetailUrlAsync(Guid userId)
|
||||
{
|
||||
var url = await appUrlProvider.GetUrlAsync(PlatformConsts.React, PlatformConsts.Urls.UserDetail);
|
||||
|
||||
return url.Contains("{0}", StringComparison.Ordinal)
|
||||
? string.Format(url, userId)
|
||||
: $"{url.TrimEnd('/')}/{userId}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Sozsoft.Platform.BlobStoring;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenIddict.Abstractions;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
|
|
@ -32,6 +34,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
||||
public IRepository<Department, Guid> departmentRepository { get; }
|
||||
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
||||
public BlobManager BlobCdnManager { get; }
|
||||
|
||||
public PlatformIdentityAppService(
|
||||
IIdentityUserAppService identityUserAppService,
|
||||
|
|
@ -45,6 +48,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
IRepository<WorkHour, Guid> workHourRepository,
|
||||
IRepository<Department, Guid> departmentRepository,
|
||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||
BlobManager blobCdnManager,
|
||||
IGuidGenerator guidGenerator
|
||||
)
|
||||
{
|
||||
|
|
@ -55,6 +59,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
this.workHourRepository = workHourRepository;
|
||||
this.departmentRepository = departmentRepository;
|
||||
this.jobPositionRepository = jobPositionRepository;
|
||||
this.BlobCdnManager = blobCdnManager;
|
||||
this.permissionRepository = permissionRepository;
|
||||
this.branchRepository = branchRepository;
|
||||
this.branchUsersRepository = branchUsersRepository;
|
||||
|
|
@ -273,6 +278,22 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
await UserManager.UpdateAsync(user);
|
||||
}
|
||||
|
||||
[Authorize(IdentityPermissions.Users.Update)]
|
||||
public async Task UpdateAvatarAsync([FromForm] UserAvatarUpdateInput input)
|
||||
{
|
||||
var user = await UserManager.GetByIdAsync(input.UserId);
|
||||
var fileName = $"{user.Id}.jpg";
|
||||
|
||||
if (input.Avatar is null || input.Avatar.ContentLength == 0)
|
||||
{
|
||||
await BlobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
await BlobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<PermissionDefinitionRecord>> GetPermissionList()
|
||||
{
|
||||
var list = await permissionRepository.GetListAsync();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Volo.Abp.Identity;
|
|||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Uow;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
private readonly ICurrentTenant _currentTenant;
|
||||
private readonly BlobManager _blobContainer;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly ILookupNormalizer _lookupNormalizer;
|
||||
private readonly IRepository<Event, Guid> _eventRepository;
|
||||
private readonly IIdentityUserAppService _identityUserAppService;
|
||||
private readonly IIdentityUserRepository _identityUserRepository;
|
||||
|
|
@ -49,7 +50,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
ICurrentTenant currentTenant,
|
||||
BlobManager blobContainer,
|
||||
IConfiguration configuration,
|
||||
|
||||
ILookupNormalizer lookupNormalizer,
|
||||
|
||||
IRepository<Event, Guid> eventRepository,
|
||||
IIdentityUserAppService identityUserAppService,
|
||||
IIdentityUserRepository identityUserRepository,
|
||||
|
|
@ -71,6 +73,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
_currentTenant = currentTenant;
|
||||
_blobContainer = blobContainer;
|
||||
_configuration = configuration;
|
||||
_lookupNormalizer = lookupNormalizer;
|
||||
_eventRepository = eventRepository;
|
||||
_identityUserAppService = identityUserAppService;
|
||||
_identityUserRepository = identityUserRepository;
|
||||
|
|
@ -114,12 +117,12 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
}
|
||||
|
||||
private UserInfoViewModel MapUserInfoViewModel(
|
||||
IdentityUser user,
|
||||
Volo.Abp.Identity.IdentityUser user,
|
||||
IReadOnlyDictionary<Guid, string> departmentDict,
|
||||
IReadOnlyDictionary<Guid, JobPosition> jobPositionDict)
|
||||
{
|
||||
return ObjectMapper
|
||||
.Map<IdentityUser, UserInfoViewModel>(user)
|
||||
.Map<Volo.Abp.Identity.IdentityUser, UserInfoViewModel>(user)
|
||||
.MapDepartmentAndJobPositionAssignments(departmentDict, jobPositionDict);
|
||||
}
|
||||
|
||||
|
|
@ -127,9 +130,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
IReadOnlyDictionary<Guid, string> departmentDict,
|
||||
IReadOnlyDictionary<Guid, JobPosition> jobPositionDict)
|
||||
{
|
||||
var normalizedAdmin = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
|
||||
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(normalizedAdmin)
|
||||
?? await _identityUserRepository.FindByNormalizedEmailAsync(normalizedAdmin);
|
||||
var normalizedAdmin = _lookupNormalizer.NormalizeName(PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue);
|
||||
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(normalizedAdmin);
|
||||
|
||||
if (user == null && CurrentUser.Id.HasValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Volo.Abp.Identity;
|
||||
using Volo.Abp.TenantManagement;
|
||||
using Volo.Abp.Data;
|
||||
using IdentityUser = Volo.Abp.Identity.IdentityUser;
|
||||
|
||||
namespace Sozsoft.Platform.ListForms.DynamicApi;
|
||||
|
||||
|
|
@ -18,79 +20,125 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
|
|||
private readonly ITenantManager tenantManager;
|
||||
private readonly IIdentityUserAppService identityUserAppService;
|
||||
private readonly IIdentityRoleAppService identityRoleAppService;
|
||||
private readonly IdentityUserManager userManager;
|
||||
private readonly IOptions<IdentityOptions> identityOptions;
|
||||
|
||||
public ListFormDynamicApiAppService(
|
||||
ITenantRepository tenantRepository,
|
||||
ITenantManager tenantManager,
|
||||
IIdentityUserAppService identityUserAppService,
|
||||
IIdentityRoleAppService identityRoleAppService)
|
||||
IIdentityRoleAppService identityRoleAppService,
|
||||
IdentityUserManager userManager,
|
||||
IOptions<IdentityOptions> identityOptions)
|
||||
{
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.tenantManager = tenantManager;
|
||||
this.identityUserAppService = identityUserAppService;
|
||||
this.identityRoleAppService = identityRoleAppService;
|
||||
this.userManager = userManager;
|
||||
this.identityOptions = identityOptions;
|
||||
}
|
||||
|
||||
private static Guid ParseGuid(string value)
|
||||
{
|
||||
return Guid.TryParse(value, out var id) ? id : Guid.Empty;
|
||||
}
|
||||
|
||||
[Authorize(IdentityPermissions.Users.Create)]
|
||||
public async Task PostUserInsertAsync(DynamicApiBaseInput<CreateUpdateUserInput> input)
|
||||
{
|
||||
var user = new IdentityUserCreateDto
|
||||
{
|
||||
UserName = input.Data.Email,
|
||||
Name = input.Data.Name,
|
||||
Surname = input.Data.Surname,
|
||||
Email = input.Data.Email,
|
||||
PhoneNumber = input.Data.PhoneNumber,
|
||||
IsActive = input.Data.IsActive ?? true,
|
||||
LockoutEnabled = true,
|
||||
Password = input.Data.Password,
|
||||
RoleNames = [], //input.Data.RoleNames,
|
||||
};
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.WorkHour, input.Data.WorkHour);
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, input.Data.DepartmentId);
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, input.Data.JobPositionId);
|
||||
await identityOptions.SetAsync();
|
||||
|
||||
await identityUserAppService.CreateAsync(user);
|
||||
var verifySetting = await SettingProvider.GetOrNullAsync(PlatformConsts.AbpIdentity.Profile.General.RequireVerifiedAccount);
|
||||
var verify = !string.Equals(verifySetting, "true", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var user = new IdentityUser(
|
||||
GuidGenerator.Create(),
|
||||
input.Data.Email,
|
||||
input.Data.Email,
|
||||
CurrentTenant.Id)
|
||||
{
|
||||
Name = input.Data.Name,
|
||||
Surname = input.Data.Surname
|
||||
};
|
||||
|
||||
user.SetIsActive(input.Data.IsActive ?? true);
|
||||
user.SetPhoneNumber(input.Data.PhoneNumber, user.PhoneNumberConfirmed);
|
||||
user.SetWorkHour(input.Data.WorkHour);
|
||||
user.SetDepartmentId(ParseGuid(input.Data.DepartmentId));
|
||||
user.SetJobPositionId(ParseGuid(input.Data.JobPositionId));
|
||||
user.SetIsVerified(verify);
|
||||
|
||||
(await userManager.CreateAsync(user, input.Data.Password)).CheckErrors();
|
||||
await userManager.SetLockoutEnabledAsync(user, true);
|
||||
}
|
||||
|
||||
[Authorize(IdentityPermissions.Users.Update)]
|
||||
public async Task PostUserUpdateAsync(DynamicApiBaseInput<CreateUpdateUserInput> input)
|
||||
{
|
||||
await identityOptions.SetAsync();
|
||||
|
||||
if (input.Keys.IsNullOrEmpty())
|
||||
{
|
||||
throw new UserFriendlyException(L["RecordNotFound"]);
|
||||
}
|
||||
var id = Guid.Parse(input.Keys[0]!.ToString()!);
|
||||
var entity = await identityUserAppService.GetAsync(id) ?? throw new EntityNotFoundException(L["RecordNotFound"]);
|
||||
var user = new IdentityUserUpdateDto
|
||||
var user = await userManager.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
UserName = input.Data.Email ?? entity.Email,
|
||||
Email = input.Data.Email ?? entity.Email,
|
||||
Name = input.Data.Name ?? entity.Name,
|
||||
Surname = input.Data.Surname ?? entity.Surname,
|
||||
PhoneNumber = input.Data.PhoneNumber ?? entity.PhoneNumber,
|
||||
IsActive = input.Data.IsActive ?? entity.IsActive,
|
||||
LockoutEnabled = input.Data.LockoutEnabled ?? entity.LockoutEnabled,
|
||||
//RoleNames = input.Data.RoleNames ?? identity.RoleNames,
|
||||
};
|
||||
if (!input.Data.Password.IsNullOrWhiteSpace())
|
||||
{
|
||||
user.Password = input.Data.Password;
|
||||
}
|
||||
if (input.Data.WorkHour != null)
|
||||
{
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.WorkHour, input.Data.WorkHour);
|
||||
}
|
||||
if (input.Data.DepartmentId != null)
|
||||
{
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, input.Data.DepartmentId);
|
||||
}
|
||||
if (input.Data.JobPositionId != null)
|
||||
{
|
||||
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, input.Data.JobPositionId);
|
||||
throw new EntityNotFoundException(L["RecordNotFound"]);
|
||||
}
|
||||
|
||||
await identityUserAppService.UpdateAsync(id, user);
|
||||
if (!input.Data.Email.IsNullOrWhiteSpace() && input.Data.Email != user.Email)
|
||||
{
|
||||
(await userManager.SetUserNameAsync(user, input.Data.Email)).CheckErrors();
|
||||
(await userManager.SetEmailAsync(user, input.Data.Email)).CheckErrors();
|
||||
}
|
||||
|
||||
user.Name = input.Data.Name ?? user.Name;
|
||||
user.Surname = input.Data.Surname ?? user.Surname;
|
||||
|
||||
if (input.Data.PhoneNumber != null)
|
||||
{
|
||||
user.SetPhoneNumber(input.Data.PhoneNumber, user.PhoneNumberConfirmed);
|
||||
}
|
||||
|
||||
if (input.Data.IsActive.HasValue)
|
||||
{
|
||||
user.SetIsActive(input.Data.IsActive.Value);
|
||||
}
|
||||
|
||||
if (input.Data.LockoutEnabled.HasValue)
|
||||
{
|
||||
(await userManager.SetLockoutEnabledAsync(user, input.Data.LockoutEnabled.Value)).CheckErrors();
|
||||
}
|
||||
|
||||
if (!input.Data.Password.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (await userManager.HasPasswordAsync(user))
|
||||
{
|
||||
(await userManager.RemovePasswordAsync(user)).CheckErrors();
|
||||
}
|
||||
|
||||
(await userManager.AddPasswordAsync(user, input.Data.Password)).CheckErrors();
|
||||
}
|
||||
|
||||
if (input.Data.WorkHour != null)
|
||||
{
|
||||
user.SetWorkHour(input.Data.WorkHour);
|
||||
}
|
||||
|
||||
if (input.Data.DepartmentId != null)
|
||||
{
|
||||
user.SetDepartmentId(ParseGuid(input.Data.DepartmentId));
|
||||
}
|
||||
|
||||
if (input.Data.JobPositionId != null)
|
||||
{
|
||||
user.SetJobPositionId(ParseGuid(input.Data.JobPositionId));
|
||||
}
|
||||
|
||||
(await userManager.UpdateAsync(user)).CheckErrors();
|
||||
}
|
||||
|
||||
//RoleAppService
|
||||
|
|
@ -197,6 +245,5 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
|
|||
var id = Guid.Parse(input.Keys[0]!.ToString()!);
|
||||
await tenantRepository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ using Volo.Abp.Domain.Repositories;
|
|||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.PermissionManagement;
|
||||
using Volo.Abp.Uow;
|
||||
using static Sozsoft.Platform.PlatformConsts;
|
||||
using System.Data;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Sozsoft.Languages;
|
||||
using Sozsoft.Platform.DynamicData;
|
||||
|
|
@ -60,10 +58,21 @@ public class ListFormWizardAppService(
|
|||
public async Task Create(ListFormWizardDto input)
|
||||
{
|
||||
var wizardName = input.WizardName.Trim();
|
||||
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
|
||||
var nameLangKey = WizardConsts.WizardKey(wizardName);
|
||||
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
|
||||
var code = WizardConsts.WizardKey(wizardName);
|
||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
||||
? WizardConsts.WizardKey(wizardName)
|
||||
: input.MenuCode.Trim();
|
||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
||||
? code
|
||||
: input.ListFormCode.Trim();
|
||||
var titleLangKey = $"{listFormCode}.Title";
|
||||
var nameLangKey = code;
|
||||
var descLangKey = $"{listFormCode}.Desc";
|
||||
var permCreateName = $"{code}.Create";
|
||||
var permUpdateName = $"{code}.Update";
|
||||
var permDeleteName = $"{code}.Delete";
|
||||
var permExportName = $"{code}.Export";
|
||||
var permImportName = $"{code}.Import";
|
||||
var permNoteName = $"{code}.Note";
|
||||
|
||||
// Eklenen kayıtları takip et (silme işleminde kullanılır)
|
||||
var inserted = new WizardInsertedRecordsDto();
|
||||
|
|
@ -78,16 +87,15 @@ public class ListFormWizardAppService(
|
|||
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
|
||||
{
|
||||
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
|
||||
await CreateLangKey(groupName, groupName, groupName, inserted);
|
||||
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
|
||||
await EnsureLangKey(groupName, inserted);
|
||||
else
|
||||
await CreateLangKey(groupName, groupName, groupName, inserted);
|
||||
inserted.PermissionGroupNames.Add(groupName);
|
||||
}
|
||||
|
||||
// Permission'ları tek seferde kontrol et ve oluştur
|
||||
var queryable = await repoPerm.GetQueryableAsync();
|
||||
var existingPerms = await AsyncExecuter.ToListAsync(
|
||||
queryable.Where(a => a.GroupName == groupName)
|
||||
);
|
||||
|
||||
var existingPerms = await repoPerm.GetListAsync(a => a.GroupName == groupName);
|
||||
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
|
||||
if (permRead == null)
|
||||
{
|
||||
|
|
@ -95,45 +103,45 @@ public class ListFormWizardAppService(
|
|||
inserted.PermissionNames.Add(permRead.Name);
|
||||
}
|
||||
|
||||
var permCreate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermCreate(wizardName));
|
||||
var permCreate = existingPerms.FirstOrDefault(a => a.Name == permCreateName);
|
||||
if (permCreate == null)
|
||||
{
|
||||
permCreate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermCreate(wizardName), permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
|
||||
permCreate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permCreate.Name);
|
||||
}
|
||||
|
||||
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermUpdate(wizardName));
|
||||
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == permUpdateName);
|
||||
if (permUpdate == null)
|
||||
{
|
||||
permUpdate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermUpdate(wizardName), permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
|
||||
permUpdate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permUpdate.Name);
|
||||
}
|
||||
|
||||
var permDelete = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermDelete(wizardName));
|
||||
var permDelete = existingPerms.FirstOrDefault(a => a.Name == permDeleteName);
|
||||
if (permDelete == null)
|
||||
{
|
||||
permDelete = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermDelete(wizardName), permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
|
||||
permDelete = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permDelete.Name);
|
||||
}
|
||||
|
||||
var permExport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermExport(wizardName));
|
||||
var permExport = existingPerms.FirstOrDefault(a => a.Name == permExportName);
|
||||
if (permExport == null)
|
||||
{
|
||||
permExport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermExport(wizardName), permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
|
||||
permExport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permExport.Name);
|
||||
}
|
||||
|
||||
var permImport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermImport(wizardName));
|
||||
var permImport = existingPerms.FirstOrDefault(a => a.Name == permImportName);
|
||||
if (permImport == null)
|
||||
{
|
||||
permImport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermImport(wizardName), permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
|
||||
permImport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permImport.Name);
|
||||
}
|
||||
|
||||
var permNote = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermNote(wizardName));
|
||||
var permNote = existingPerms.FirstOrDefault(a => a.Name == permNoteName);
|
||||
if (permNote == null)
|
||||
{
|
||||
permNote = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermNote(wizardName), permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
|
||||
permNote = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
|
||||
inserted.PermissionNames.Add(permNote.Name);
|
||||
}
|
||||
|
||||
|
|
@ -161,12 +169,18 @@ public class ListFormWizardAppService(
|
|||
if (menuParent == null)
|
||||
{
|
||||
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
||||
await CreateLangKey(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
|
||||
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 = WizardConsts.WizardKeyParent(wizardName),
|
||||
DisplayName = input.MenuParentCode,
|
||||
IsDisabled = false,
|
||||
Icon = menuParentIcon,
|
||||
Order = maxRootOrder + 1,
|
||||
}, autoSave: false);
|
||||
inserted.MenuCodes.Add(input.MenuParentCode);
|
||||
|
|
@ -218,7 +232,7 @@ public class ListFormWizardAppService(
|
|||
ColSpan = g.ColCount,
|
||||
ItemType = "group",
|
||||
Items = g.Items
|
||||
.Where(i => i.DataField != input.KeyFieldName)
|
||||
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
|
||||
.Select((it, ii) => new EditingFormItemDto
|
||||
{
|
||||
Order = ii + 1,
|
||||
|
|
@ -231,6 +245,7 @@ public class ListFormWizardAppService(
|
|||
})
|
||||
.ToArray()
|
||||
})
|
||||
.Where(g => g.Items.Length > 0)
|
||||
.ToList();
|
||||
|
||||
//ListForm - varsa sil, yeniden ekle
|
||||
|
|
@ -257,14 +272,15 @@ public class ListFormWizardAppService(
|
|||
var isCreated = tableColumns.Contains("CreatorId");
|
||||
input.Workflow ??= new WorkflowDto();
|
||||
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
||||
|
||||
var listForm = await repoListForm.InsertAsync(new ListForm
|
||||
await repoListForm.InsertAsync(new ListForm
|
||||
{
|
||||
ListFormType = ListFormTypeEnum.List,
|
||||
PageSize = 10,
|
||||
ExportJson = WizardConsts.DefaultExportJson,
|
||||
IsSubForm = false,
|
||||
ShowNote = input.SubForms.Count > 0,
|
||||
ShowNote = input.SubForms.Count > 0 || input.WorkflowCriteria.Count > 0,
|
||||
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler),
|
||||
CultureName = LanguageCodes.En,
|
||||
ListFormCode = input.ListFormCode,
|
||||
|
|
@ -280,12 +296,12 @@ public class ListFormWizardAppService(
|
|||
KeyFieldName = input.KeyFieldName,
|
||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
||||
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
SortMode = PlatformConsts.GridOptions.SortModeSingle,
|
||||
FilterRowJson = WizardConsts.DefaultFilterRowJson,
|
||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
||||
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson,
|
||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? PlatformConsts.GridOptions.SelectionModeSingle : PlatformConsts.GridOptions.SelectionModeNone),
|
||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
||||
|
|
@ -395,13 +411,65 @@ public class ListFormWizardAppService(
|
|||
{
|
||||
return workflow != null && (
|
||||
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
|
||||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
|
||||
criteria.Count > 0
|
||||
);
|
||||
}
|
||||
|
||||
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
||||
{
|
||||
if (criteria == null || criteria.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var baseTitles = criteria
|
||||
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
||||
.ToList();
|
||||
var duplicateTitles = baseTitles
|
||||
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.Where(x => x.Count() > 1)
|
||||
.Select(x => x.Key)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var item in criteria)
|
||||
{
|
||||
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
||||
var title = baseTitle;
|
||||
|
||||
if (duplicateTitles.Contains(baseTitle))
|
||||
{
|
||||
titleCounts.TryGetValue(baseTitle, out var count);
|
||||
count++;
|
||||
titleCounts[baseTitle] = count;
|
||||
title = $"{baseTitle}{count}";
|
||||
}
|
||||
|
||||
if (usedTitles.Contains(title))
|
||||
{
|
||||
var index = 1;
|
||||
var candidate = $"{title}{index}";
|
||||
while (usedTitles.Contains(candidate))
|
||||
{
|
||||
index++;
|
||||
candidate = $"{title}{index}";
|
||||
}
|
||||
|
||||
title = candidate;
|
||||
}
|
||||
|
||||
item.Title = title;
|
||||
usedTitles.Add(title);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeWorkflowTitleFallback(string kind)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
||||
/// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar.
|
||||
|
|
@ -677,4 +745,16 @@ public class ListFormWizardAppService(
|
|||
|
||||
return existing;
|
||||
}
|
||||
|
||||
private async Task<LanguageKey> EnsureLangKey(string key, WizardInsertedRecordsDto inserted = null)
|
||||
{
|
||||
var res = PlatformConsts.AppName;
|
||||
|
||||
var existing = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == key);
|
||||
if (existing != null) return existing;
|
||||
|
||||
existing = await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key }, autoSave: true);
|
||||
inserted?.LanguageKeys.Add(key);
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ using System.Text.Json;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Sozsoft.Platform.Data.Seeds;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Sozsoft.Platform.ListForms.Select;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
using Sozsoft.Sender.Mail;
|
||||
using Volo.Abp;
|
||||
|
|
@ -29,6 +31,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
private const string SystemApprovalDescription = "Sistem tarafından otomatik olarak onaylandı.";
|
||||
|
||||
private readonly IRepository<ListFormWorkflow, string> criteriaRepository;
|
||||
private readonly IRepository<Note, Guid> noteRepository;
|
||||
private readonly IListFormManager listFormManager;
|
||||
private readonly IListFormAuthorizationManager authManager;
|
||||
private readonly IListFormSelectAppService listFormSelectAppService;
|
||||
|
|
@ -36,18 +39,22 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
private readonly IdentityUserManager identityUserManager;
|
||||
private readonly ISozsoftEmailSender erpEmailSender;
|
||||
private readonly ISettingProvider settingProvider;
|
||||
private readonly IStringLocalizer<PlatformResource> localizer;
|
||||
|
||||
public ListFormWorkflowAppService(
|
||||
IRepository<ListFormWorkflow, string> criteriaRepository,
|
||||
IRepository<Note, Guid> noteRepository,
|
||||
IListFormManager listFormManager,
|
||||
IListFormAuthorizationManager authManager,
|
||||
IListFormSelectAppService listFormSelectAppService,
|
||||
IQueryManager queryManager,
|
||||
IdentityUserManager identityUserManager,
|
||||
ISozsoftEmailSender erpEmailSender,
|
||||
ISettingProvider settingProvider)
|
||||
ISettingProvider settingProvider,
|
||||
IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
this.criteriaRepository = criteriaRepository;
|
||||
this.noteRepository = noteRepository;
|
||||
this.listFormManager = listFormManager;
|
||||
this.authManager = authManager;
|
||||
this.listFormSelectAppService = listFormSelectAppService;
|
||||
|
|
@ -55,6 +62,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
this.identityUserManager = identityUserManager;
|
||||
this.erpEmailSender = erpEmailSender;
|
||||
this.settingProvider = settingProvider;
|
||||
this.localizer = localizer;
|
||||
}
|
||||
|
||||
[HttpGet("criteria")]
|
||||
|
|
@ -188,7 +196,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
|
||||
criteria.ListFormCode = code;
|
||||
criteria.Kind = NormalizeRequired(input.Kind, "Compare");
|
||||
criteria.Title = NormalizeRequired(input.Title, criteria.Kind);
|
||||
criteria.Title = await NormalizeUniqueTitleAsync(code, criteria.Id, input.Kind, input.Title);
|
||||
criteria.CompareColumn = NormalizeRequired(input.CompareColumn, "Price");
|
||||
criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">");
|
||||
criteria.CompareValue = input.CompareValue;
|
||||
|
|
@ -300,7 +308,14 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
|
||||
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
|
||||
|
||||
return await RunUntilWaitAsync(context, start);
|
||||
context.WorkflowNoteRows.Add(("Started By: ", ResolveCurrentUserDisplayName()));
|
||||
var result = await RunUntilWaitAsync(context, start);
|
||||
await InsertWorkflowNoteAsync(
|
||||
context,
|
||||
$"Workflow Started: {start.Title}",
|
||||
BuildWorkflowNoteContent(context.WorkflowNoteRows));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[HttpPost("decision")]
|
||||
|
|
@ -365,7 +380,15 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
}
|
||||
|
||||
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
|
||||
return await RunUntilWaitAsync(context, next);
|
||||
context.WorkflowNoteRows.Add(("Decision By: ", ResolveCurrentUserDisplayName()));
|
||||
AddWorkflowDecisionRows(context, current, input.Approved, input.Note ?? string.Empty);
|
||||
var result = await RunUntilWaitAsync(context, next);
|
||||
await InsertWorkflowNoteAsync(
|
||||
context,
|
||||
$"Workflow {(input.Approved ? "Approved" : "Rejected")}: {current.Title}",
|
||||
BuildWorkflowNoteContent(context.WorkflowNoteRows));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<WorkflowRunResultDto> RunForEachKeyAsync(
|
||||
|
|
@ -387,7 +410,10 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
CurrentNodeTitle = last?.CurrentNodeTitle,
|
||||
CurrentNodeKind = last?.CurrentNodeKind,
|
||||
WaitingApproval = results.Any(x => x.WaitingApproval),
|
||||
Completed = results.All(x => x.Completed)
|
||||
Completed = results.All(x => x.Completed),
|
||||
ToastMessages = results
|
||||
.SelectMany(result => result.ToastMessages ?? [])
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +445,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
.ToList();
|
||||
var row = await GetRowAsync(code, listForm.KeyFieldName, keys[0]);
|
||||
|
||||
return new WorkflowRunContext(code, listForm.KeyFieldName, keys, workflow, criteria, row);
|
||||
return new WorkflowRunContext(code, keys, workflow, criteria, row);
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, object>> GetRowAsync(string listFormCode, string keyFieldName, object key)
|
||||
|
|
@ -511,6 +537,8 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
|
||||
await UpdateRowAsync(context, update);
|
||||
MergeRowValues(context.Row, update);
|
||||
AddWorkflowNodeRows(context, node);
|
||||
AddWorkflowToastMessage(context, node);
|
||||
|
||||
if (node.Kind == "Inform")
|
||||
{
|
||||
|
|
@ -533,7 +561,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
recipientEmail,
|
||||
sender,
|
||||
new { },
|
||||
BuildInformEmailBody(context, node),
|
||||
BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context)),
|
||||
$"Workflow Bilgilendirme: {node.Title}",
|
||||
null,
|
||||
true);
|
||||
|
|
@ -542,6 +570,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
{
|
||||
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task<string> ResolveApproverEmailAsync(string approver)
|
||||
|
|
@ -565,22 +594,257 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
return user.Email;
|
||||
}
|
||||
|
||||
private static string BuildInformEmailBody(WorkflowRunContext context, ListFormWorkflow node)
|
||||
private async Task<string> BuildPreviousWorkflowNotesHtmlAsync(WorkflowRunContext context)
|
||||
{
|
||||
var key = context.Keys?.FirstOrDefault()?.ToString();
|
||||
if (key.IsNullOrWhiteSpace())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var notes = (await noteRepository.GetListAsync(note =>
|
||||
note.EntityName == context.ListFormCode &&
|
||||
note.EntityId == key &&
|
||||
note.Type == "workflow"))
|
||||
.OrderBy(note => note.CreationTime)
|
||||
.ToList();
|
||||
|
||||
if (notes.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var noteItems = notes.Select(note =>
|
||||
$"""
|
||||
<div style="margin: 0 0 12px 0; padding: 10px 12px; border: 1px solid #e5e7eb; border-radius: 6px;">
|
||||
<div style="font-weight: 600; margin-bottom: 6px;">{Encode(note.Subject)}</div>
|
||||
<div>{note.Content}</div>
|
||||
</div>
|
||||
""");
|
||||
|
||||
return string.Join(string.Empty, noteItems);
|
||||
}
|
||||
|
||||
private static string BuildInformEmailBody(
|
||||
WorkflowRunContext context,
|
||||
ListFormWorkflow node,
|
||||
string previousWorkflowNotesHtml)
|
||||
{
|
||||
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
||||
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
||||
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? string.Empty);
|
||||
var recipient = WebUtility.HtmlEncode(node.Approver ?? string.Empty);
|
||||
var processRows = BuildWorkflowNoteContent(context.WorkflowNoteRows);
|
||||
var previousNotesSection = previousWorkflowNotesHtml.IsNullOrWhiteSpace()
|
||||
? string.Empty
|
||||
: $"""
|
||||
<h3 style="margin: 18px 0 8px 0;">Önceki Workflow Notları</h3>
|
||||
{previousWorkflowNotesHtml}
|
||||
""";
|
||||
|
||||
return $"""
|
||||
<p>Workflow bilgilendirme adimina ulasildi.</p>
|
||||
<table>
|
||||
<tr><td><strong>Liste Formu</strong></td><td>{listFormCode}</td></tr>
|
||||
<tr><td><strong>Adim</strong></td><td>{nodeTitle}</td></tr>
|
||||
<tr><td><strong>Kayit</strong></td><td>{keyText}</td></tr>
|
||||
</table>
|
||||
<div style="font-family: Arial, sans-serif; color: #111827; line-height: 1.45;">
|
||||
<p>Workflow sürecinde bilgilendirme adımına ulaşıldı.</p>
|
||||
<table style="border-collapse: collapse; width: 100%; margin-bottom: 16px;">
|
||||
<tr><td style="padding: 6px 8px;"><strong>Liste Formu</strong></td><td style="padding: 6px 8px;">{listFormCode}</td></tr>
|
||||
<tr><td style="padding: 6px 8px;"><strong>Kayıt</strong></td><td style="padding: 6px 8px;">{keyText}</td></tr>
|
||||
<tr><td style="padding: 6px 8px;"><strong>Bilgilendirme Adımı</strong></td><td style="padding: 6px 8px;">{nodeTitle}</td></tr>
|
||||
<tr><td style="padding: 6px 8px;"><strong>Alıcı</strong></td><td style="padding: 6px 8px;">{recipient}</td></tr>
|
||||
</table>
|
||||
|
||||
<h3 style="margin: 18px 0 8px 0;">Bu İşlemdeki Süreç Özeti</h3>
|
||||
{processRows}
|
||||
|
||||
{previousNotesSection}
|
||||
</div>
|
||||
""";
|
||||
}
|
||||
|
||||
private string ResolveCurrentUserDisplayName()
|
||||
{
|
||||
return CurrentUser.UserName
|
||||
?? CurrentUser.Name
|
||||
?? CurrentUser.Id?.ToString()
|
||||
?? "System";
|
||||
}
|
||||
|
||||
private static void AddWorkflowDecisionRows(
|
||||
WorkflowRunContext context,
|
||||
ListFormWorkflow current,
|
||||
bool approved,
|
||||
string description)
|
||||
{
|
||||
var action = approved ? "Approved" : "Rejected";
|
||||
context.WorkflowNoteRows.Add(("Decision: ", $"{action}: {FormatNode(current)}"));
|
||||
context.WorkflowNoteRows.Add(("Description: ", description ?? string.Empty));
|
||||
}
|
||||
|
||||
private static void AddWorkflowNodeRows(
|
||||
WorkflowRunContext context,
|
||||
ListFormWorkflow node)
|
||||
{
|
||||
var action = node.Kind switch
|
||||
{
|
||||
"Start" => "Started",
|
||||
"Compare" => "Evaluated",
|
||||
"Approval" => "Waiting Approval",
|
||||
"Inform" => "Informed",
|
||||
"End" => "Completed",
|
||||
_ => "Processed"
|
||||
};
|
||||
|
||||
context.WorkflowNoteRows.Add(($"{action}: ", $"{FormatNode(node)}"));
|
||||
|
||||
if (!node.Approver.IsNullOrWhiteSpace())
|
||||
{
|
||||
context.WorkflowNoteRows.Add((node.Kind == "Inform" ? "Inform: " : "Approver: ", node.Approver));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddWorkflowToastMessage(
|
||||
WorkflowRunContext context,
|
||||
ListFormWorkflow node)
|
||||
{
|
||||
var userName = ResolveCurrentUserDisplayName();
|
||||
var next = FindNextToastNode(context, node);
|
||||
var messageLines = node.Kind switch
|
||||
{
|
||||
"Start" => new[]
|
||||
{
|
||||
localizer["ListForms.ListForm.Workflow.WorkflowStarted"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
||||
}.Concat(FormatNextToastNode(next)),
|
||||
"Inform" => new[]
|
||||
{
|
||||
localizer["ListForms.ListForm.Workflow.InformReached"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
||||
}.Concat(FormatNextToastNode(next)),
|
||||
"End" => new[]
|
||||
{
|
||||
localizer["ListForms.ListForm.Workflow.WorkflowCompleted"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
||||
},
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (messageLines != null)
|
||||
{
|
||||
context.ToastMessages.Add(string.Join(Environment.NewLine, messageLines));
|
||||
}
|
||||
}
|
||||
|
||||
private ListFormWorkflow FindNextToastNode(
|
||||
WorkflowRunContext context,
|
||||
ListFormWorkflow node)
|
||||
{
|
||||
var current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, node));
|
||||
var visited = new HashSet<string>();
|
||||
|
||||
while (current != null && visited.Add(current.Id))
|
||||
{
|
||||
if (current.Kind is "Approval" or "Inform" or "End")
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, current));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IEnumerable<string> FormatNextToastNode(ListFormWorkflow node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return node.Kind switch
|
||||
{
|
||||
"Approval" =>
|
||||
[
|
||||
localizer["ListForms.ListForm.Workflow.NextStep.Approval"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
||||
localizer["ListForms.ListForm.Workflow.ApproverUser", FormatToastUser(node.Approver)].Value
|
||||
],
|
||||
"Inform" =>
|
||||
[
|
||||
localizer["ListForms.ListForm.Workflow.NextStep.Inform"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
||||
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
||||
],
|
||||
"End" =>
|
||||
[
|
||||
localizer["ListForms.ListForm.Workflow.NextStep.End"].Value,
|
||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value
|
||||
],
|
||||
_ => [localizer["ListForms.ListForm.Workflow.NextStep", node.Title].Value]
|
||||
};
|
||||
}
|
||||
|
||||
private string FormatToastUser(string userName)
|
||||
{
|
||||
return userName.IsNullOrWhiteSpace()
|
||||
? localizer["ListForms.ListForm.Workflow.UndefinedUser"].Value
|
||||
: userName;
|
||||
}
|
||||
|
||||
private async Task InsertWorkflowNoteAsync(
|
||||
WorkflowRunContext context,
|
||||
string subject,
|
||||
string content)
|
||||
{
|
||||
var key = context.Keys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var note = new Note(GuidGenerator.Create())
|
||||
{
|
||||
TenantId = CurrentTenant.Id,
|
||||
EntityName = context.ListFormCode,
|
||||
EntityId = key.ToString(),
|
||||
Type = "workflow",
|
||||
Subject = subject,
|
||||
Content = content,
|
||||
FilesJson = "[]"
|
||||
};
|
||||
|
||||
await noteRepository.InsertAsync(note, autoSave: true);
|
||||
}
|
||||
|
||||
private static string BuildWorkflowNoteContent(List<(string Label, string Value)> rows)
|
||||
{
|
||||
var tableRows = rows
|
||||
.Where(row => !row.Value.IsNullOrWhiteSpace())
|
||||
.Select(row =>
|
||||
$"<tr><td><strong>{Encode(row.Label)}</strong></td><td>{Encode(row.Value)}</td></tr>");
|
||||
|
||||
return $"<table class=\"workflow-note-log\">{string.Join(string.Empty, tableRows)}</table>";
|
||||
}
|
||||
|
||||
private static string FormatNode(ListFormWorkflow node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var title = node.Title ?? string.Empty;
|
||||
var kind = node.Kind ?? string.Empty;
|
||||
return $"{title} ({kind} - {node.Id})";
|
||||
}
|
||||
|
||||
private static string Encode(string value)
|
||||
{
|
||||
return WebUtility.HtmlEncode(value ?? string.Empty);
|
||||
}
|
||||
|
||||
private async Task UpdateRowAsync(WorkflowRunContext context, Dictionary<string, object> data)
|
||||
{
|
||||
await queryManager.GenerateAndRunQueryAsync<int>(
|
||||
|
|
@ -762,7 +1026,8 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
CurrentNodeTitle = node?.Title,
|
||||
CurrentNodeKind = node?.Kind,
|
||||
WaitingApproval = waitingApproval,
|
||||
Completed = completed
|
||||
Completed = completed,
|
||||
ToastMessages = context.ToastMessages.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -906,6 +1171,36 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
return value.IsNullOrWhiteSpace() ? fallback : value.Trim();
|
||||
}
|
||||
|
||||
private async Task<string> NormalizeUniqueTitleAsync(
|
||||
string listFormCode,
|
||||
string criteriaId,
|
||||
string kind,
|
||||
string title)
|
||||
{
|
||||
var baseTitle = NormalizeRequired(title, kind);
|
||||
var existingTitles = (await criteriaRepository.GetListAsync(x =>
|
||||
x.ListFormCode == listFormCode &&
|
||||
x.Id != criteriaId))
|
||||
.Select(x => x.Title?.Trim())
|
||||
.Where(x => !x.IsNullOrWhiteSpace())
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (!existingTitles.Contains(baseTitle))
|
||||
{
|
||||
return baseTitle;
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
var candidate = $"{baseTitle}{index}";
|
||||
while (existingTitles.Contains(candidate))
|
||||
{
|
||||
index++;
|
||||
candidate = $"{baseTitle}{index}";
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private static string SerializeCompareOutcomes(List<CompareOutcomeDto> outcomes)
|
||||
{
|
||||
return JsonSerializer.Serialize(outcomes ?? []);
|
||||
|
|
@ -947,12 +1242,14 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
|||
|
||||
private sealed record WorkflowRunContext(
|
||||
string ListFormCode,
|
||||
string KeyFieldName,
|
||||
|
||||
object[] Keys,
|
||||
WorkflowDto Workflow,
|
||||
List<ListFormWorkflow> Criteria,
|
||||
IDictionary<string, object> Row)
|
||||
{
|
||||
public HashSet<string> UserUpdatedFields { get; } = [];
|
||||
public List<(string Label, string Value)> WorkflowNoteRows { get; } = [];
|
||||
public List<string> ToastMessages { get; } = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,15 @@ public class MenuAppService : CrudAppService<
|
|||
private readonly IRepository<LanguageText, Guid> _repositoryText;
|
||||
private readonly ITenantRepository _tenantRepository;
|
||||
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
|
||||
private readonly LanguageTextAppService _languageTextAppService;
|
||||
|
||||
public MenuAppService(
|
||||
IRepository<Menu, Guid> menuRepository,
|
||||
IRepository<LanguageKey, Guid> languageKeyRepository,
|
||||
IRepository<LanguageText, Guid> languageTextRepository,
|
||||
ITenantRepository tenantRepository,
|
||||
IPermissionDefinitionRecordRepository permissionRepository
|
||||
IPermissionDefinitionRecordRepository permissionRepository,
|
||||
LanguageTextAppService languageTextAppService
|
||||
) : base(menuRepository)
|
||||
{
|
||||
_menuRepository = menuRepository;
|
||||
|
|
@ -44,6 +46,7 @@ public class MenuAppService : CrudAppService<
|
|||
_repositoryText = languageTextRepository;
|
||||
_tenantRepository = tenantRepository;
|
||||
_permissionRepository = permissionRepository;
|
||||
_languageTextAppService = languageTextAppService;
|
||||
|
||||
CreatePolicyName = $"{AppCodes.Menus.Menu}.Create";
|
||||
UpdatePolicyName = $"{AppCodes.Menus.Menu}.Update";
|
||||
|
|
@ -275,7 +278,7 @@ public class MenuAppService : CrudAppService<
|
|||
if (existingEnText != null)
|
||||
{
|
||||
existingEnText.Value = input.MenuTextEn;
|
||||
await _repositoryText.UpdateAsync(existingEnText);
|
||||
await _repositoryText.UpdateAsync(existingEnText, autoSave: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -285,7 +288,7 @@ public class MenuAppService : CrudAppService<
|
|||
CultureName = "en",
|
||||
Value = input.MenuTextEn,
|
||||
ResourceName = PlatformConsts.AppName
|
||||
});
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
// Türkçe text oluşturuluyor veya güncelleniyor.
|
||||
|
|
@ -297,7 +300,7 @@ public class MenuAppService : CrudAppService<
|
|||
if (existingTrText != null)
|
||||
{
|
||||
existingTrText.Value = input.MenuTextTr;
|
||||
await _repositoryText.UpdateAsync(existingTrText);
|
||||
await _repositoryText.UpdateAsync(existingTrText, autoSave: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -307,9 +310,12 @@ public class MenuAppService : CrudAppService<
|
|||
CultureName = "tr",
|
||||
Value = input.MenuTextTr,
|
||||
ResourceName = PlatformConsts.AppName
|
||||
});
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
// Clear Redis Cache
|
||||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
|
||||
return await base.CreateAsync(input);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,20 +195,13 @@ public class PublicAppService : PlatformAppService
|
|||
|
||||
public async Task<HomeDto> GetHomeAsync()
|
||||
{
|
||||
var entity = await _homeRepository.FirstOrDefaultAsync();
|
||||
if (entity == null)
|
||||
{
|
||||
entity = await _homeRepository.InsertAsync(CreateDefaultHomeEntity(), autoSave: true);
|
||||
}
|
||||
|
||||
var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home));
|
||||
return ObjectMapper.Map<Home, HomeDto>(entity);
|
||||
}
|
||||
|
||||
public async Task SaveHomePageAsync(SaveHomePageInput input)
|
||||
{
|
||||
var entity = await _homeRepository.FirstOrDefaultAsync();
|
||||
var isNewEntity = entity == null;
|
||||
entity ??= CreateDefaultHomeEntity();
|
||||
var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home));
|
||||
|
||||
entity.HeroBackgroundImageKey = input.HeroBackgroundImageKey;
|
||||
entity.HeroPrimaryCtaKey = input.HeroPrimaryCtaKey;
|
||||
|
|
@ -252,14 +245,7 @@ public class PublicAppService : PlatformAppService
|
|||
StyleClass = solution.StyleClass,
|
||||
}).ToList());
|
||||
|
||||
if (isNewEntity)
|
||||
{
|
||||
await _homeRepository.InsertAsync(entity, autoSave: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _homeRepository.UpdateAsync(entity, autoSave: false);
|
||||
}
|
||||
await _homeRepository.UpdateAsync(entity, autoSave: false);
|
||||
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroBackgroundImageKey, input.HeroBackgroundImageValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaKey, input.HeroPrimaryCtaValue);
|
||||
|
|
@ -608,83 +594,6 @@ public class PublicAppService : PlatformAppService
|
|||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
}
|
||||
|
||||
private static Home CreateDefaultHomeEntity()
|
||||
{
|
||||
var slides = new List<HomeSlideDto>
|
||||
{
|
||||
new()
|
||||
{
|
||||
TitleKey = "Public.hero.slide1.title",
|
||||
SubtitleKey = "Public.hero.slide1.subtitle",
|
||||
Services = new List<HomeSlideServiceDto>
|
||||
{
|
||||
new() { Icon = "FaCalendarAlt", TitleKey = "Public.hero.slide1.service1.title", DescriptionKey = "Public.hero.slide1.service1.desc" },
|
||||
new() { Icon = "FaUsers", TitleKey = "Public.hero.slide1.service2.title", DescriptionKey = "Public.hero.slide1.service2.desc" },
|
||||
new() { Icon = "FaShieldAlt", TitleKey = "Public.hero.slide1.service3.title", DescriptionKey = "Public.hero.slide1.service3.desc" },
|
||||
},
|
||||
},
|
||||
new()
|
||||
{
|
||||
TitleKey = "Public.hero.slide2.title",
|
||||
SubtitleKey = "Public.hero.slide2.subtitle",
|
||||
Services = new List<HomeSlideServiceDto>
|
||||
{
|
||||
new() { Icon = "FaChartBar", TitleKey = "Public.hero.slide2.service1.title", DescriptionKey = "Public.hero.slide2.service1.desc" },
|
||||
new() { Icon = "FaCreditCard", TitleKey = "Public.hero.slide2.service2.title", DescriptionKey = "Public.hero.slide2.service2.desc" },
|
||||
new() { Icon = "FaDatabase", TitleKey = "Public.hero.slide2.service3.title", DescriptionKey = "Public.hero.slide2.service3.desc" },
|
||||
},
|
||||
},
|
||||
new()
|
||||
{
|
||||
TitleKey = "Public.hero.slide3.title",
|
||||
SubtitleKey = "Public.hero.slide3.subtitle",
|
||||
Services = new List<HomeSlideServiceDto>
|
||||
{
|
||||
new() { Icon = "FaDesktop", TitleKey = "Public.hero.slide3.service1.title", DescriptionKey = "Public.hero.slide3.service1.desc" },
|
||||
new() { Icon = "FaServer", TitleKey = "Public.hero.slide3.service2.title", DescriptionKey = "Public.hero.slide3.service2.desc" },
|
||||
new() { Icon = "FaMobileAlt", TitleKey = "Public.hero.slide3.service3.title", DescriptionKey = "Public.hero.slide3.service3.desc" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var features = new List<HomeFeatureDto>
|
||||
{
|
||||
new() { Icon = "FaUsers", TitleKey = "Public.features.reliable", DescriptionKey = "Public.features.reliable.desc" },
|
||||
new() { Icon = "FaCalendarAlt", TitleKey = "App.Videoroom.Planning", DescriptionKey = "Public.features.rapid.desc" },
|
||||
new() { Icon = "FaBookOpen", TitleKey = "Public.features.expert", DescriptionKey = "Public.features.expert.desc" },
|
||||
new() { Icon = "FaCreditCard", TitleKey = "Public.features.muhasebe", DescriptionKey = "Public.features.muhasebe.desc" },
|
||||
new() { Icon = "FaRegComment", TitleKey = "Public.features.iletisim", DescriptionKey = "Public.features.iletisim.desc" },
|
||||
new() { Icon = "FaPhone", TitleKey = "Public.features.mobil", DescriptionKey = "Public.features.mobil.desc" },
|
||||
new() { Icon = "FaChartBar", TitleKey = "Public.features.scalable", DescriptionKey = "Public.features.scalable.desc" },
|
||||
new() { Icon = "FaShieldAlt", TitleKey = "Public.features.guvenlik", DescriptionKey = "Public.features.guvenlik.desc" },
|
||||
};
|
||||
|
||||
var solutions = new List<HomeSolutionDto>
|
||||
{
|
||||
new() { Icon = "FaDesktop", ColorClass = "bg-blue-600", TitleKey = "Public.services.web.title", DescriptionKey = "Public.solutions.web.desc" },
|
||||
new() { Icon = "FaMobileAlt", ColorClass = "bg-purple-600", TitleKey = "Public.services.mobile.title", DescriptionKey = "Public.solutions.mobile.desc" },
|
||||
new() { Icon = "FaServer", ColorClass = "bg-green-600", TitleKey = "Public.solutions.custom.title", DescriptionKey = "Public.solutions.custom.desc" },
|
||||
new() { Icon = "FaDatabase", ColorClass = "bg-red-600", TitleKey = "Public.solutions.database.title", DescriptionKey = "Public.solutions.database.desc" },
|
||||
};
|
||||
|
||||
return new Home
|
||||
{
|
||||
HeroBackgroundImageKey = "Public.home.hero.backgroundImage",
|
||||
HeroPrimaryCtaKey = "Public.hero.cta.consultation",
|
||||
HeroSecondaryCtaKey = "Public.hero.cta.discover",
|
||||
FeaturesTitleKey = "Public.features.title",
|
||||
FeaturesSubtitleKey = "Public.features.subtitle",
|
||||
SolutionsTitleKey = "Public.solutions.title",
|
||||
SolutionsSubtitleKey = "Public.solutions.subtitle",
|
||||
CtaTitleKey = "Public.common.getStarted",
|
||||
CtaSubtitleKey = "Public.common.contact",
|
||||
CtaButtonLabelKey = "Public.common.learnMore",
|
||||
SlidesJson = JsonSerializer.Serialize(slides),
|
||||
FeaturesJson = JsonSerializer.Serialize(features),
|
||||
SolutionsJson = JsonSerializer.Serialize(solutions),
|
||||
};
|
||||
}
|
||||
|
||||
private async Task UpsertLanguageTextAsync(string cultureName, string key, string value)
|
||||
{
|
||||
if (key.IsNullOrWhiteSpace())
|
||||
|
|
|
|||
|
|
@ -8,13 +8,101 @@
|
|||
}
|
||||
],
|
||||
"Settings": [
|
||||
{
|
||||
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||
"descriptionKey": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
|
||||
"defaultValue": "system@sozsoft.com",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.General",
|
||||
"requiredPermissionName": "App.SiteManagement.General",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"code": "App.SiteManagement.General.TimedLoginEmails",
|
||||
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
|
||||
"descriptionKey": "App.SiteManagement.General.TimedLoginEmails.Description",
|
||||
"defaultValue": "system@sozsoft.com",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.General",
|
||||
"requiredPermissionName": "App.SiteManagement.General",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"code": "App.SiteManagement.Theme.Style",
|
||||
"nameKey": "App.SiteManagement.Theme.Style",
|
||||
"descriptionKey": "App.SiteManagement.Theme.Style.Description",
|
||||
"defaultValue": "dx.material.blue.light.compact",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "U|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.Theme",
|
||||
"requiredPermissionName": "App.SiteManagement.Theme",
|
||||
"dataType": "List",
|
||||
"selectOptions": {
|
||||
"dx.light": "App.Setting.light",
|
||||
"dx.light.compact": "App.Setting.light.compact",
|
||||
"dx.dark": "App.Setting.dark",
|
||||
"dx.dark.compact": "App.Setting.dark.compact",
|
||||
"dx.contrast": "App.Setting.contrast",
|
||||
"dx.contrast.compact": "App.Setting.contrast.compact",
|
||||
"dx.carmine": "App.Setting.carmine",
|
||||
"dx.darkmoon": "App.Setting.darkmoon",
|
||||
"dx.softblue": "App.Setting.softblue",
|
||||
"dx.darkviolet": "App.Setting.darkviolet",
|
||||
"dx.greenmist": "App.Setting.greenmist",
|
||||
"dx.material.blue.light": "App.Setting.material.blue.light",
|
||||
"dx.material.blue.dark": "App.Setting.material.blue.dark",
|
||||
"dx.material.lime.light": "App.Setting.material.lime.light",
|
||||
"dx.material.lime.dark": "App.Setting.material.lime.dark",
|
||||
"dx.material.orange.light": "App.Setting.material.orange.light",
|
||||
"dx.material.orange.dark": "App.Setting.material.orange.dark",
|
||||
"dx.material.purple.light": "App.Setting.material.purple.light",
|
||||
"dx.material.purple.dark": "App.Setting.material.purple.dark",
|
||||
"dx.material.teal.light": "App.Setting.material.teal.light",
|
||||
"dx.material.teal.dark": "App.Setting.material.teal.dark",
|
||||
"dx.material.blue.light.compact": "App.Setting.material.blue.light.compact",
|
||||
"dx.material.blue.dark.compact": "App.Setting.material.blue.dark.compact",
|
||||
"dx.material.lime.light.compact": "App.Setting.material.lime.light.compact",
|
||||
"dx.material.lime.dark.compact": "App.Setting.material.lime.dark.compact",
|
||||
"dx.material.orange.light.compact": "App.Setting.material.orange.light.compact",
|
||||
"dx.material.orange.dark.compact": "App.Setting.material.orange.dark.compact",
|
||||
"dx.material.purple.light.compact": "App.Setting.material.purple.light.compact",
|
||||
"dx.material.purple.dark.compact": "App.Setting.material.purple.dark.compact",
|
||||
"dx.material.teal.light.compact": "App.Setting.material.teal.light.compact",
|
||||
"dx.material.teal.dark.compact": "App.Setting.material.teal.dark.compact",
|
||||
"dx.fluent.blue.dark.compact": "App.Setting.fluent.blue.dark.compact",
|
||||
"dx.fluent.blue.dark": "App.Setting.fluent.blue.dark",
|
||||
"dx.fluent.blue.light.compact": "App.Setting.fluent.blue.light.compact",
|
||||
"dx.fluent.blue.light": "App.Setting.fluent.blue.light",
|
||||
"dx.fluent.saas.dark.compact": "App.Setting.fluent.saas.dark.compact",
|
||||
"dx.fluent.saas.dark": "App.Setting.fluent.saas.dark",
|
||||
"dx.fluent.saas.light.compact": "App.Setting.fluent.saas.light.compact",
|
||||
"dx.fluent.saas.light": "App.Setting.fluent.saas.light"
|
||||
},
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"code": "Abp.Localization.DefaultLanguage",
|
||||
"nameKey": "Abp.Localization.DefaultLanguage",
|
||||
"descriptionKey": "Abp.Localization.DefaultLanguage.Description",
|
||||
"defaultValue": "tr",
|
||||
"defaultValue": "en",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
|
|
@ -40,7 +128,7 @@
|
|||
"tr": "Türkçe",
|
||||
"zh-Hans": "繁體中文"
|
||||
},
|
||||
"order": 1
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"code": "Abp.Timing.TimeZone",
|
||||
|
|
@ -48,7 +136,7 @@
|
|||
"descriptionKey": "Abp.Timing.TimeZone.Description",
|
||||
"defaultValue": "Turkey Standard Time",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
|
|
@ -196,94 +284,6 @@
|
|||
"Yakutsk Standard Time": "Yakutsk Standard Time (+09:00)",
|
||||
"Yukon Standard Time": "Yukon Standard Time (-07:00)"
|
||||
},
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"code": "App.SiteManagement.Theme.Style",
|
||||
"nameKey": "App.SiteManagement.Theme.Style",
|
||||
"descriptionKey": "App.SiteManagement.Theme.Style.Description",
|
||||
"defaultValue": "dx.material.blue.light.compact",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "U|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.Theme",
|
||||
"requiredPermissionName": "App.SiteManagement.Theme",
|
||||
"dataType": "List",
|
||||
"selectOptions": {
|
||||
"dx.light": "App.Setting.light",
|
||||
"dx.light.compact": "App.Setting.light.compact",
|
||||
"dx.dark": "App.Setting.dark",
|
||||
"dx.dark.compact": "App.Setting.dark.compact",
|
||||
"dx.contrast": "App.Setting.contrast",
|
||||
"dx.contrast.compact": "App.Setting.contrast.compact",
|
||||
"dx.carmine": "App.Setting.carmine",
|
||||
"dx.darkmoon": "App.Setting.darkmoon",
|
||||
"dx.softblue": "App.Setting.softblue",
|
||||
"dx.darkviolet": "App.Setting.darkviolet",
|
||||
"dx.greenmist": "App.Setting.greenmist",
|
||||
"dx.material.blue.light": "App.Setting.material.blue.light",
|
||||
"dx.material.blue.dark": "App.Setting.material.blue.dark",
|
||||
"dx.material.lime.light": "App.Setting.material.lime.light",
|
||||
"dx.material.lime.dark": "App.Setting.material.lime.dark",
|
||||
"dx.material.orange.light": "App.Setting.material.orange.light",
|
||||
"dx.material.orange.dark": "App.Setting.material.orange.dark",
|
||||
"dx.material.purple.light": "App.Setting.material.purple.light",
|
||||
"dx.material.purple.dark": "App.Setting.material.purple.dark",
|
||||
"dx.material.teal.light": "App.Setting.material.teal.light",
|
||||
"dx.material.teal.dark": "App.Setting.material.teal.dark",
|
||||
"dx.material.blue.light.compact": "App.Setting.material.blue.light.compact",
|
||||
"dx.material.blue.dark.compact": "App.Setting.material.blue.dark.compact",
|
||||
"dx.material.lime.light.compact": "App.Setting.material.lime.light.compact",
|
||||
"dx.material.lime.dark.compact": "App.Setting.material.lime.dark.compact",
|
||||
"dx.material.orange.light.compact": "App.Setting.material.orange.light.compact",
|
||||
"dx.material.orange.dark.compact": "App.Setting.material.orange.dark.compact",
|
||||
"dx.material.purple.light.compact": "App.Setting.material.purple.light.compact",
|
||||
"dx.material.purple.dark.compact": "App.Setting.material.purple.dark.compact",
|
||||
"dx.material.teal.light.compact": "App.Setting.material.teal.light.compact",
|
||||
"dx.material.teal.dark.compact": "App.Setting.material.teal.dark.compact",
|
||||
"dx.fluent.blue.dark.compact": "App.Setting.fluent.blue.dark.compact",
|
||||
"dx.fluent.blue.dark": "App.Setting.fluent.blue.dark",
|
||||
"dx.fluent.blue.light.compact": "App.Setting.fluent.blue.light.compact",
|
||||
"dx.fluent.blue.light": "App.Setting.fluent.blue.light",
|
||||
"dx.fluent.saas.dark.compact": "App.Setting.fluent.saas.dark.compact",
|
||||
"dx.fluent.saas.dark": "App.Setting.fluent.saas.dark",
|
||||
"dx.fluent.saas.light.compact": "App.Setting.fluent.saas.light.compact",
|
||||
"dx.fluent.saas.light": "App.Setting.fluent.saas.light"
|
||||
},
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||
"descriptionKey": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
|
||||
"defaultValue": "SYSTEM@SOZSOFT.COM",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.General",
|
||||
"requiredPermissionName": "App.SiteManagement.General",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"code": "App.SiteManagement.General.TimedLoginEmails",
|
||||
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
|
||||
"descriptionKey": "App.SiteManagement.General.TimedLoginEmails.Description",
|
||||
"defaultValue": "SYSTEM@SOZSOFT.COM",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "App.SiteManagement",
|
||||
"subGroupKey": "App.SiteManagement.General",
|
||||
"requiredPermissionName": "App.SiteManagement.General",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
|
|
@ -466,7 +466,7 @@
|
|||
"code": "Abp.Mailing.DefaultFromAddress",
|
||||
"nameKey": "Abp.Mailing.DefaultFromAddress",
|
||||
"descriptionKey": "Abp.Mailing.DefaultFromAddress.Description",
|
||||
"defaultValue": "SYSTEM@SOZSOFT.COM",
|
||||
"defaultValue": "system@sozsoft.com",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -482,7 +482,7 @@
|
|||
"code": "Abp.Mailing.Smtp.UserName",
|
||||
"nameKey": "Abp.Mailing.Smtp.UserName",
|
||||
"descriptionKey": "Abp.Mailing.Smtp.UserName.Description",
|
||||
"defaultValue": "SYSTEM@SOZSOFT.COM",
|
||||
"defaultValue": "system@sozsoft.com",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -574,6 +574,22 @@
|
|||
"selectOptions": {},
|
||||
"order": 27
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.Smtp.UseDefaultCredentials",
|
||||
"nameKey": "Abp.Mailing.Smtp.UseDefaultCredentials",
|
||||
"descriptionKey": "Abp.Mailing.Smtp.UseDefaultCredentials.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": true,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Mailing",
|
||||
"subGroupKey": "Abp.Mailing.Smtp",
|
||||
"requiredPermissionName": "Abp.Mailing.Smtp",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 28
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.AWS.Profile",
|
||||
"nameKey": "Abp.Mailing.AWS.Profile",
|
||||
|
|
@ -588,7 +604,7 @@
|
|||
"requiredPermissionName": "Abp.Mailing.AWS",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 28
|
||||
"order": 29
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.AWS.Region",
|
||||
|
|
@ -604,29 +620,13 @@
|
|||
"requiredPermissionName": "Abp.Mailing.AWS",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 29
|
||||
"order": 30
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.AWS.AccessKey",
|
||||
"nameKey": "Abp.Mailing.AWS.AccessKey",
|
||||
"descriptionKey": "Abp.Mailing.AWS.AccessKey.Description",
|
||||
"defaultValue": "aXW8L21rP6dPO6Txj76Be2FCpWRBa25EMrSAVL76",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Mailing",
|
||||
"subGroupKey": "Abp.Mailing.AWS",
|
||||
"requiredPermissionName": "Abp.Mailing.AWS",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 30
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.AWS.AccessKeyId",
|
||||
"nameKey": "Abp.Mailing.AWS.AccessKeyId",
|
||||
"descriptionKey": "Abp.Mailing.AWS.AccessKeyId.Description",
|
||||
"defaultValue": "AKIATULUYBLX4IY3S2P1",
|
||||
"defaultValue": "SibFBAMiSApvz+NChYmlgZmx25JNbximemIDOFps",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -638,11 +638,27 @@
|
|||
"selectOptions": {},
|
||||
"order": 31
|
||||
},
|
||||
{
|
||||
"code": "Abp.Mailing.AWS.AccessKeyId",
|
||||
"nameKey": "Abp.Mailing.AWS.AccessKeyId",
|
||||
"descriptionKey": "Abp.Mailing.AWS.AccessKeyId.Description",
|
||||
"defaultValue": "AKIA5OCSDJB5KOQY74NV",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Mailing",
|
||||
"subGroupKey": "Abp.Mailing.AWS",
|
||||
"requiredPermissionName": "Abp.Mailing.AWS",
|
||||
"dataType": "Text",
|
||||
"selectOptions": {},
|
||||
"order": 32
|
||||
},
|
||||
{
|
||||
"code": "Abp.Account.IsSelfRegistrationEnabled",
|
||||
"nameKey": "Abp.Account.IsSelfRegistrationEnabled",
|
||||
"descriptionKey": "Abp.Account.IsSelfRegistrationEnabled.Description",
|
||||
"defaultValue": "True",
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -674,8 +690,8 @@
|
|||
"code": "Abp.Account.TwoFactor.Enabled",
|
||||
"nameKey": "Abp.Account.TwoFactor.Enabled",
|
||||
"descriptionKey": "Abp.Account.TwoFactor.Enabled.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": false,
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
|
|
@ -722,7 +738,7 @@
|
|||
"code": "Abp.Account.Captcha.SiteKey",
|
||||
"nameKey": "Abp.Account.Captcha.SiteKey",
|
||||
"descriptionKey": "Abp.Account.Captcha.SiteKey.Description",
|
||||
"defaultValue": "0x4AAAAAAAGadwQME-GSYuJU",
|
||||
"defaultValue": "0x4AAAAAABdEjmiXxcl0j7jp",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -738,7 +754,7 @@
|
|||
"code": "Abp.Account.Captcha.SecretKey",
|
||||
"nameKey": "Abp.Account.Captcha.SecretKey",
|
||||
"descriptionKey": "Abp.Account.Captcha.SecretKey.Description",
|
||||
"defaultValue": "0x4AAAAAAAGad_f_WP47IcNBs9FTu5DhNX8",
|
||||
"defaultValue": "0x4AAAAAABdEjhw1A8sJZUvQX8-CgqvB3mE",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -754,8 +770,8 @@
|
|||
"code": "Abp.Identity.Profile.General.RequireVerifiedAccount",
|
||||
"nameKey": "Abp.Identity.Profile.General.RequireVerifiedAccount",
|
||||
"descriptionKey": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": false,
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
|
|
@ -782,6 +798,104 @@
|
|||
"selectOptions": {},
|
||||
"order": 51
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.RequireConfirmedEmail",
|
||||
"nameKey": "Abp.Identity.SignIn.RequireConfirmedEmail",
|
||||
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedEmail.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 60
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.RequireEmailVerificationToRegister",
|
||||
"nameKey": "Abp.Identity.SignIn.RequireEmailVerificationToRegister",
|
||||
"descriptionKey": "Abp.Identity.SignIn.RequireEmailVerificationToRegister.Description",
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": true,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 61
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber",
|
||||
"nameKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber",
|
||||
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber.Description",
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 62
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation",
|
||||
"nameKey": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation",
|
||||
"descriptionKey": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation.Description",
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": true,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 63
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.User.IsUserNameUpdateEnabled",
|
||||
"nameKey": "Abp.Identity.User.IsUserNameUpdateEnabled",
|
||||
"descriptionKey": "Abp.Identity.User.IsUserNameUpdateEnabled.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.User",
|
||||
"requiredPermissionName": "Abp.Identity.User",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 64
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.User.IsEmailUpdateEnabled",
|
||||
"nameKey": "Abp.Identity.User.IsEmailUpdateEnabled",
|
||||
"descriptionKey": "Abp.Identity.User.IsEmailUpdateEnabled.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.User",
|
||||
"requiredPermissionName": "Abp.Identity.User",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 65
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"code": "Abp.Identity.Password.ForceUsersToPeriodicallyChangePassword",
|
||||
"nameKey": "Abp.Identity.Password.ForceUsersToPeriodicallyChangePassword",
|
||||
|
|
@ -796,13 +910,13 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 52
|
||||
"order": 70
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.PasswordChangePeriodDays",
|
||||
"nameKey": "Abp.Identity.Password.PasswordChangePeriodDays",
|
||||
"descriptionKey": "Abp.Identity.Password.PasswordChangePeriodDays.Description",
|
||||
"defaultValue": "0",
|
||||
"defaultValue": "180",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": false,
|
||||
|
|
@ -812,7 +926,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 53
|
||||
"order": 71
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequiredLength",
|
||||
|
|
@ -828,7 +942,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 54
|
||||
"order": 72
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequiredUniqueChars",
|
||||
|
|
@ -844,7 +958,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 55
|
||||
"order": 73
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequireNonAlphanumeric",
|
||||
|
|
@ -860,7 +974,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 56
|
||||
"order": 74
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequireLowercase",
|
||||
|
|
@ -876,7 +990,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 57
|
||||
"order": 75
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequireUppercase",
|
||||
|
|
@ -892,7 +1006,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 58
|
||||
"order": 76
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Password.RequireDigit",
|
||||
|
|
@ -908,7 +1022,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Password",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 59
|
||||
"order": 77
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Lockout.AllowedForNewUsers",
|
||||
|
|
@ -924,7 +1038,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Lockout",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 60
|
||||
"order": 78
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Lockout.LockoutDuration",
|
||||
|
|
@ -940,7 +1054,7 @@
|
|||
"requiredPermissionName": "Abp.Identity.Lockout",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 61
|
||||
"order": 79
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.Lockout.MaxFailedAccessAttempts",
|
||||
|
|
@ -956,71 +1070,23 @@
|
|||
"requiredPermissionName": "Abp.Identity.Lockout",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 62
|
||||
"order": 80
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.RequireConfirmedEmail",
|
||||
"nameKey": "Abp.Identity.SignIn.RequireConfirmedEmail",
|
||||
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedEmail.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"code": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
|
||||
"nameKey": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
|
||||
"descriptionKey": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount.Description",
|
||||
"defaultValue": "2147483647",
|
||||
"isVisibleToClients": true,
|
||||
"providers": "T|G|D",
|
||||
"isInherited": true,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"subGroupKey": "Abp.Identity.OrganizationUnits",
|
||||
"requiredPermissionName": "Abp.Identity.OrganizationUnits",
|
||||
"dataType": "Number",
|
||||
"selectOptions": {},
|
||||
"order": 63
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber",
|
||||
"nameKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber",
|
||||
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber.Description",
|
||||
"defaultValue": "False",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.SignIn",
|
||||
"requiredPermissionName": "Abp.Identity.SignIn",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 64
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.User.IsUserNameUpdateEnabled",
|
||||
"nameKey": "Abp.Identity.User.IsUserNameUpdateEnabled",
|
||||
"descriptionKey": "Abp.Identity.User.IsUserNameUpdateEnabled.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.User",
|
||||
"requiredPermissionName": "Abp.Identity.User",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 65
|
||||
},
|
||||
{
|
||||
"code": "Abp.Identity.User.IsEmailUpdateEnabled",
|
||||
"nameKey": "Abp.Identity.User.IsEmailUpdateEnabled",
|
||||
"descriptionKey": "Abp.Identity.User.IsEmailUpdateEnabled.Description",
|
||||
"defaultValue": "True",
|
||||
"isVisibleToClients": false,
|
||||
"providers": "G|D",
|
||||
"isInherited": false,
|
||||
"isEncrypted": false,
|
||||
"mainGroupKey": "Abp.Identity",
|
||||
"subGroupKey": "Abp.Identity.User",
|
||||
"requiredPermissionName": "Abp.Identity.User",
|
||||
"dataType": "Bool",
|
||||
"selectOptions": {},
|
||||
"order": 66
|
||||
"order": 90
|
||||
}
|
||||
],
|
||||
"NotificationTypes": [],
|
||||
|
|
|
|||
|
|
@ -948,6 +948,12 @@
|
|||
"en": "No record found...",
|
||||
"tr": "Kayıt Bulunamadı..."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Definitions.WorkHour",
|
||||
"en": "Work Hours",
|
||||
"tr": "Çalışma Saatleri"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Definitions.Sequence",
|
||||
|
|
@ -1923,8 +1929,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Url.Description",
|
||||
"en": "Url Description",
|
||||
"tr": "Url Açıklaması"
|
||||
"en": "Posta Güvercini URL is usually in the format: http://api.postaguvercini.com/v1/sms/send?username={username}&password={password}&to={to}&message={message}",
|
||||
"tr": "Posta Güvercini URL'si genellikle şu formatta olur: http://api.postaguvercini.com/v1/sms/send?username={username}&password={password}&to={to}&message={message}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -1935,8 +1941,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Sms.PostaGuvercini.Username.Description",
|
||||
"en": "User Name Description",
|
||||
"tr": "Kullanıcı Adı Açıklaması"
|
||||
"en": "The user name for the Posta Güvercini service.",
|
||||
"tr": "Posta Güvercini hizmeti için kullanıcı adı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -1947,8 +1953,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Sms.PostaGuvercini.Password.Description",
|
||||
"en": "Password Description",
|
||||
"tr": "Parola Açıklaması"
|
||||
"en": "The password for the Posta Güvercini service.",
|
||||
"tr": "Posta Güvercini hizmeti için parola."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -1971,8 +1977,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.WhatsApp.PhoneNumberId.Description",
|
||||
"en": "PhoneNumberId Description",
|
||||
"tr": "PhoneNumberId Description"
|
||||
"en": "The phone number ID for the WhatsApp service.",
|
||||
"tr": "WhatsApp hizmeti için telefon numarası ID'si."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -1983,8 +1989,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.WhatsApp.TemplateName.Description",
|
||||
"en": "Template Name Description",
|
||||
"tr": "Şablon Adı Açıklaması"
|
||||
"en": "The name of the template for the WhatsApp service.",
|
||||
"tr": "WhatsApp hizmeti için şablon adı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -1995,8 +2001,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.WhatsApp.Token.Description",
|
||||
"en": "Token Description",
|
||||
"tr": "Token Açıklaması"
|
||||
"en": "The token for the WhatsApp service.",
|
||||
"tr": "WhatsApp hizmeti için token."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2019,20 +2025,14 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Rocket.UserId.Description",
|
||||
"en": "User Id Description",
|
||||
"tr": "User Id Açıklaması"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Rocket.Token",
|
||||
"en": "Token",
|
||||
"tr": "Token"
|
||||
"en": "The user ID for the Rocket Chat service.",
|
||||
"tr": "Rocket Chat hizmeti için kullanıcı ID'si."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Sender.Rocket.Token.Description",
|
||||
"en": "Token Description",
|
||||
"tr": "Token Açıklaması"
|
||||
"en": "The token for the Rocket Chat service.",
|
||||
"tr": "Rocket Chat hizmeti için token."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2055,8 +2055,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.DefaultFromDisplayName.Description",
|
||||
"en": "From Display Name Description",
|
||||
"tr": "Varsayılan İsim Açıklaması"
|
||||
"en": "Default name to be shown when sending mails. Usually it is company name or application name.",
|
||||
"tr": "Mail gönderilirken görünecek varsayılan isim. Genellikle şirket adı veya uygulama adı olur."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2067,8 +2067,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.DefaultFromAddress.Description",
|
||||
"en": "From Address Description",
|
||||
"tr": "Varsayılan Email Adresi Açıklaması"
|
||||
"en": "Default email address to be shown when sending mails. Usually it is company email or application email.",
|
||||
"tr": "Mail gönderilirken görünecek varsayılan email adresi. Genellikle şirket emaili veya uygulama emaili olur."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2085,8 +2085,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.Host.Description",
|
||||
"en": "Host Description",
|
||||
"tr": "Host Açıklaması"
|
||||
"en": "The host address of the SMTP server.",
|
||||
"tr": "SMTP sunucusunun ana bilgisayar adresi."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2097,8 +2097,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.Port.Description",
|
||||
"en": "Port Description",
|
||||
"tr": "Port Açıklaması"
|
||||
"en": "The port number of the SMTP server.",
|
||||
"tr": "SMTP sunucusunun port numarası."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2109,8 +2109,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.UserName.Description",
|
||||
"en": "User Name Description",
|
||||
"tr": "Kullanıcı Adı Açıklaması"
|
||||
"en": "The user name for the SMTP server.",
|
||||
"tr": "SMTP sunucusu için kullanıcı adı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2121,8 +2121,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.Password.Description",
|
||||
"en": "Password Description",
|
||||
"tr": "Parola Açıklaması"
|
||||
"en": "The password for the SMTP server.",
|
||||
"tr": "SMTP sunucusu için parola."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2133,8 +2133,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.Domain.Description",
|
||||
"en": "Domain Description",
|
||||
"tr": "Alan Adı Açıklaması"
|
||||
"en": "The domain for the SMTP server.",
|
||||
"tr": "SMTP sunucusu için alan adı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2145,8 +2145,32 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.EnableSsl.Description",
|
||||
"en": "Enable Ssl Description",
|
||||
"tr": "Ssl Etkinleştir Açıklaması"
|
||||
"en": "Whether to enable SSL for the SMTP server.",
|
||||
"tr": "SMTP sunucusu için SSL etkinleştirilip etkinleştirilmeyeceğini belirtir."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.UseDefaultCredentials",
|
||||
"en": "Use Default Credentials",
|
||||
"tr": "Varsayılan Kimlik Bilgilerini Kullan"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.Smtp.UseDefaultCredentials.Description",
|
||||
"en": "Whether to use default credentials for the SMTP server.",
|
||||
"tr": "SMTP sunucusu için varsayılan kimlik bilgilerini kullanıp kullanmayacağını belirtir."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
|
||||
"en": "Max User Membership Count",
|
||||
"tr": "Maksimum Kullanıcı Üyelik Sayısı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount.Description",
|
||||
"en": "Maximum number of users that can be members of each organization unit. 0 means unlimited.",
|
||||
"tr": "Her bir organizasyon biriminin sahip olabileceği maksimum kullanıcı sayısı. 0 means unlimited."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2163,8 +2187,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.AWS.Profile.Description",
|
||||
"en": "Profile Description",
|
||||
"tr": "Profil Açıklaması"
|
||||
"en": "The profile for the AWS SES service.",
|
||||
"tr": "AWS SES hizmeti için profil."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2175,8 +2199,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.AWS.Region.Description",
|
||||
"en": "Region Description",
|
||||
"tr": "Bölge Açıklaması"
|
||||
"en": "The region of the AWS SES service.",
|
||||
"tr": "AWS SES hizmetinin bölgesi."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2187,8 +2211,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.AWS.AccessKey.Description",
|
||||
"en": "Access Key Description",
|
||||
"tr": "Erişim Anahtarı Açıklaması"
|
||||
"en": "The access key for the AWS SES service.",
|
||||
"tr": "AWS SES hizmeti için erişim anahtarı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2199,8 +2223,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Mailing.AWS.AccessKeyId.Description",
|
||||
"en": "Access KeyId Description",
|
||||
"tr": "Erişim Anahtarı Kimliği Açıklaması"
|
||||
"en": "The access key ID for the AWS SES service.",
|
||||
"tr": "AWS SES hizmeti için erişim anahtarı kimliği."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2223,8 +2247,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SiteManagement.Theme.Style.Description",
|
||||
"en": "Style Description",
|
||||
"tr": "Stil Açıklaması"
|
||||
"en": "The style for the site theme.",
|
||||
"tr": "Site temasi için stil."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2241,8 +2265,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
|
||||
"en": "New Member Notification Emails Description",
|
||||
"tr": "Yeni Üye Bildirim Mailleri Açıklaması"
|
||||
"en": "The emails sent to new members.",
|
||||
"tr": "Yeni üelere gönderilen mailler."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2253,8 +2277,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SiteManagement.General.TimedLoginEmails.Description",
|
||||
"en": "Timed Login Emails Description",
|
||||
"tr": "Süreli Giriş Mailleri Açıklaması"
|
||||
"en": "The emails sent for timed login notifications.",
|
||||
"tr": "Süreli giriş bildirimleri için gönderilen mailler."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2271,8 +2295,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Localization.DefaultLanguage.Description",
|
||||
"en": "Default Language Description",
|
||||
"tr": "Varsayılan Dil Açıklaması"
|
||||
"en": "Default language of the application. It is used as a fallback when a translation for the current language is not found.",
|
||||
"tr": "Uygulamanın varsayılan dili. Geçerli dil için bir çeviri bulunamadığında yedek olarak kullanılır."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2289,8 +2313,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Localization.Timezone.Description",
|
||||
"en": "Timezone Description",
|
||||
"tr": "Saat Dilimi Açıklaması"
|
||||
"en": "Default timezone of the application. Users' timezones are adjusted according to this value.",
|
||||
"tr": "Uygulamanın varsayılan saat dilimi. Kullanıcıların saat dilimleri bu değere göre ayarlanır."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2313,8 +2337,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.EnableLocalLogin.Description",
|
||||
"en": "Authenticate with a local account Description",
|
||||
"tr": "Yerel bir hesapla kimlik doğrulama açıklaması"
|
||||
"en": "Enables the local login option on the login page.",
|
||||
"tr": "Login sayfasında yerel giriş seçeneğini etkinleştirir."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2325,8 +2349,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.IsSelfRegistrationEnabled.Description",
|
||||
"en": "Enable self registration Description",
|
||||
"tr": "Kendi kendine kaydı etkinleştir Açıklaması"
|
||||
"en": "Enables the self-registration option on the login page.",
|
||||
"tr": "Login sayfasında kendi kendine kaydolma seçeneğini etkinleştirir."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2433,8 +2457,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.TwoFactor.Enabled.Description",
|
||||
"en": "Two Factor Enabled Description",
|
||||
"tr": "İki Faktör Ektinleştirme Açıklaması"
|
||||
"en": "When enabled, users will be required to enter a two-factor authentication code sent to their email address during login.",
|
||||
"tr": "Etkinleştirildiğinde, kullanıcıların giriş sırasında e-posta adreslerine gönderilen iki faktörlü kimlik doğrulama kodunu girmeleri gerekecektir."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2451,8 +2475,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.Captcha.MaxFailedAccessAttempts.Description",
|
||||
"en": "Max failed show Captcha Description",
|
||||
"tr": "Kaç başarısız girişte gösterilecek Açıklaması"
|
||||
"en": "The number of failed login attempts after which the CAPTCHA will be displayed.",
|
||||
"tr": "CAPTCHA'nın gösterileceği başarısız giriş denemesi sayısı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2463,8 +2487,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.Captcha.EndPoint.Description",
|
||||
"en": "End Point URL Description",
|
||||
"tr": "Doğrulama Servis URL Açıklaması"
|
||||
"en": "The URL of the CAPTCHA verification endpoint.",
|
||||
"tr": "CAPTCHA doğrulama uç noktasının URL'si."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2475,8 +2499,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.Captcha.SiteKey.Description",
|
||||
"en": "Site Key Description",
|
||||
"tr": "Site Anahtarı Açıklaması"
|
||||
"en": "The site key for the CAPTCHA service.",
|
||||
"tr": "CAPTCHA hizmeti için site anahtarı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2487,8 +2511,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Account.Captcha.SecretKey.Description",
|
||||
"en": "Private Key Description",
|
||||
"tr": "Özel Anahtarı Açıklaması"
|
||||
"en": "The private key for the CAPTCHA service.",
|
||||
"tr": "CAPTCHA hizmeti için özel anahtarı."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -2814,6 +2838,30 @@
|
|||
"en": "Whether a confirmed telephone number is required to sign in.",
|
||||
"tr": "Oturum açmak için onaylanmış bir telefon numarasının gerekip gerekmediği."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation",
|
||||
"en": "Enable Phone Number Confirmation",
|
||||
"tr": "Telefon Numarası Onayını Etkinleştir"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.SignIn.EnablePhoneNumberConfirmation.Description",
|
||||
"en": "Whether to enable phone number confirmation during sign in.",
|
||||
"tr": "Oturum açarken telefon numarası onayının etkinleştirilip etkinleştirilmeyeceği."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.SignIn.RequireEmailVerificationToRegister",
|
||||
"en": "Require Email Verification to Register",
|
||||
"tr": "Kayıt Olmak için E-posta Doğrulaması Gerektir"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.SignIn.RequireEmailVerificationToRegister.Description",
|
||||
"en": "Whether to require email verification to register.",
|
||||
"tr": "Kayıt olmak için e-posta doğrulaması gerekip gerekmediği."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.User",
|
||||
|
|
@ -3309,14 +3357,14 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.Profile.General.RequireVerifiedAccount",
|
||||
"en": "Require Verified Account",
|
||||
"tr": "Hesap Doğrulaması Zorunlu mu?"
|
||||
"en": "Require Admin Verified Account",
|
||||
"tr": "Yönetici Hesap Doğrulaması Yapacak mı?"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description",
|
||||
"en": "Require Verified Account Description",
|
||||
"tr": "Hesap Doğrulama Zorunlu Açıklaması"
|
||||
"en": "Whether to require admin verified account to display user profile",
|
||||
"tr": "Kullanıcı profilini görüntülemek için yönetici tarafından doğrulanmış hesap gerekip gerekmediği"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -3327,8 +3375,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Abp.Identity.Profile.General.BlacklistedEmailProviders.Description",
|
||||
"en": "Black listed Email Providers Description",
|
||||
"tr": "Kara Listedeki e-Posta Sağlayıcıları Açıklaması"
|
||||
"en": "Comma-separated email providers that are not allowed to be used for registration or updating email. For example: yahoo.com, hotmail.com",
|
||||
"tr": "Kayıt veya e-posta güncelleme için kullanılmaya izin verilmeyen virgülle ayrılmış e-posta sağlayıcıları. Örneğin: yahoo.com, hotmail.com"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -3594,6 +3642,36 @@
|
|||
"en": "The record was deleted",
|
||||
"tr": "Kayıt silindi"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "TumKayitlarSilindi",
|
||||
"en": "All records were deleted.",
|
||||
"tr": "Tüm kayıtlar silindi."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "SeciliKayitBekliyor",
|
||||
"en": "The selected record is not waiting for this approval step or approval user.",
|
||||
"tr": "Seçili kayit bu onay adımında veya onay kullanıcısında beklemiyor."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "WorkflowAlreadyStarted",
|
||||
"en": "Workflow has already been started for the selected record",
|
||||
"tr": "Seçili kayıt icin workflow zaten başlamış."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "SeciliKayitlarSilmekIstiyormusunuz",
|
||||
"en": "{0} records will be deleted. Are you sure you want to delete?",
|
||||
"tr": "{0} kayit silinecek. Silmek istediginize emin misiniz?"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "TumKayitlariSilmekIstiyormusunuz",
|
||||
"en": "Are you sure to delete all {0} records?",
|
||||
"tr": "Tüm {0} kayıtları silmek istediğinize emin misiniz?"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "KayitEklendi",
|
||||
|
|
@ -3687,8 +3765,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.AddNewRecord",
|
||||
"en": "Add New Record",
|
||||
"tr": "Yeni Kayıt Ekle"
|
||||
"en": "Add",
|
||||
"tr": "Ekle"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -4106,9 +4184,9 @@
|
|||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.NoteModal.Type.Activity",
|
||||
"en": "Activity",
|
||||
"tr": "Aktivite"
|
||||
"key": "ListForms.ListForm.NoteModal.Type.Workflow",
|
||||
"en": "Workflow",
|
||||
"tr": "Akış"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -7314,6 +7392,24 @@
|
|||
"tr": "Demo Talep Formu",
|
||||
"en": "Demo Request Form"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Public.demo.newDemo",
|
||||
"tr": "Yeni Demo Talebi",
|
||||
"en": "New Demo Request"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Public.demo.thankYou",
|
||||
"tr": "Teşekkürler!",
|
||||
"en": "Thank You!"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Public.demo.resultMessage",
|
||||
"tr": "Demo talebiniz başarıyla gönderildi. 24 saat içinde size geri dönüş yapacağız.",
|
||||
"en": "Your demo request has been submitted successfully. We will get back to you within 24 hours."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "Public.demo.users",
|
||||
|
|
@ -10240,7 +10336,7 @@
|
|||
"resourceName": "Platform",
|
||||
"key": "App.About",
|
||||
"tr": "Hakkımızda",
|
||||
"en": "About Us"
|
||||
"en": "About"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
@ -13856,6 +13952,12 @@
|
|||
"en": "Component Path",
|
||||
"tr": "Bileşen Yolu"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.ComponentType",
|
||||
"en": "Component Type",
|
||||
"tr": "Bileşen Türü"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.ConditionType",
|
||||
|
|
@ -16664,6 +16766,24 @@
|
|||
"en": "Approver",
|
||||
"tr": "Onayla"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.Rejecter",
|
||||
"en": "Rejecter",
|
||||
"tr": "Reddet"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.ApprovalComment",
|
||||
"en": "Approval or Rejection Comment",
|
||||
"tr": "Onay veya red açıklaması"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.WorkflowDecisionMessage",
|
||||
"en": "Workflow decision will be made for {0} record(s).",
|
||||
"tr": "{0} kayit icin workflow karari verilecek."
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.NextOnStart",
|
||||
|
|
@ -17402,6 +17522,12 @@
|
|||
"en": "Columns load after selecting a Select Command",
|
||||
"tr": "Select Command seçince sütunlar yüklenir"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step3.IncludeInEditingForm",
|
||||
"en": "Include in Editing Form",
|
||||
"tr": "Düzenleme Formunda Dahil Et"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step3.GenerateFromTable",
|
||||
|
|
@ -17642,6 +17768,12 @@
|
|||
"en": "Key Field Type",
|
||||
"tr": "Anahtar Alan Tipi"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.ColumnsAndFormLayout",
|
||||
"en": "Columns & Form Layout",
|
||||
"tr": "Sütunlar ve Form Yerleşimi"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.SelectedColumns",
|
||||
|
|
@ -17666,6 +17798,24 @@
|
|||
"en": "Field",
|
||||
"tr": "Alan"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.EditingForm",
|
||||
"en": "Popup Form",
|
||||
"tr": "Popup Form"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.EditingFormColumns",
|
||||
"en": "Popup Form Columns",
|
||||
"tr": "Popup Form Sütunları"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.UngroupedColumns",
|
||||
"en": "Ungrouped Columns",
|
||||
"tr": "Gruplanmamış Sütunlar"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.Wizard.Step4.DeployAndSave",
|
||||
|
|
@ -17966,6 +18116,12 @@
|
|||
"en": "Add Multi-Tenant Column",
|
||||
"tr": "MultiTenant Sütunları Ekle"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SqlQueryManager.AddWorkflowColumns",
|
||||
"en": "Add Workflow Column",
|
||||
"tr": "Workflow Sütunları Ekle"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SqlQueryManager.ClearAllColumns",
|
||||
|
|
@ -18914,6 +19070,12 @@
|
|||
"en": "Approval Status Field Name",
|
||||
"tr": "Onay Durumu Alanı Adı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListFormEdit.Workflow.IsFilterUserName",
|
||||
"en": "Filter User Name?",
|
||||
"tr": "Kullanıcı Adı Filtresin mi?"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListFormEdit.Workflow.ApprovalDescriptionFieldName",
|
||||
|
|
@ -19159,6 +19321,84 @@
|
|||
"key": "FileManager.SortByModifiedDesc",
|
||||
"en": "Modified (Newest)",
|
||||
"tr": "Değiştirilme (En Yeni)"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.WorkflowStarted",
|
||||
"en": "Operation: Workflow started",
|
||||
"tr": "İşlem: Workflow başlatıldı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.InformReached",
|
||||
"en": "Operation: Workflow inform step reached",
|
||||
"tr": "İşlem: Workflow bilgilendirme adımına ulaştı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.WorkflowCompleted",
|
||||
"en": "Operation: Workflow completed",
|
||||
"tr": "İşlem: Workflow tamamlandı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.Step",
|
||||
"en": "Step: {0}",
|
||||
"tr": "Adım: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.PerformedBy",
|
||||
"en": "Performed by: {0}",
|
||||
"tr": "İşlemi yapan: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.InformUser",
|
||||
"en": "User to inform: {0}",
|
||||
"tr": "Bilgilendirilecek kullanıcı: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.ApproverUser",
|
||||
"en": "Approver user: {0}",
|
||||
"tr": "Onaylayacak kullanıcı: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.NextStep.Approval",
|
||||
"en": "Next step: Approval",
|
||||
"tr": "Sonraki adım: Onay"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.NextStep.Inform",
|
||||
"en": "Next step: Inform",
|
||||
"tr": "Sonraki adım: Bilgilendirme"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.NextStep.End",
|
||||
"en": "Next step: Workflow end",
|
||||
"tr": "Sonraki adım: Workflow bitiş"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.NextStepName",
|
||||
"en": "Next step name: {0}",
|
||||
"tr": "Sonraki adım adı: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.NextStep",
|
||||
"en": "Next step: {0}",
|
||||
"tr": "Sonraki adım: {0}"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListForm.Workflow.UndefinedUser",
|
||||
"en": "Undefined",
|
||||
"tr": "Tanımsız"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -797,7 +797,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
PermissionJson = DefaultPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, listFormName, PlatformConsts.IdentityPermissions.Users.Update, PlatformConsts.IdentityPermissions.Users.Delete, PlatformConsts.IdentityPermissions.Users.Export, PlatformConsts.IdentityPermissions.Users.Import, PlatformConsts.IdentityPermissions.Users.Note),
|
||||
DeleteCommand = $"UPDATE \"AbpUsers\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 600, true, true, true, true, false),
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 710, true, true, true, true, false),
|
||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||
new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[
|
||||
new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
||||
|
|
@ -807,7 +807,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
new EditingFormItemDto { Order=5, DataField="WorkHour", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order=6, DataField="DepartmentId", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order=7, DataField="JobPositionId", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order=8, DataField="Password", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order=8, DataField="Password", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
|
||||
]}
|
||||
}),
|
||||
|
|
@ -980,6 +980,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
EditorOptions = EditorOptionValues.PhoneEditorOptions,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
|
||||
|
|
@ -1540,7 +1541,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
}),
|
||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||
new() { FieldName = "Status", FieldDbType = DbType.String, Value = "Aktif", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "Status", FieldDbType = DbType.String, Value = "active", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
}),
|
||||
CommandColumnJson = JsonSerializer.Serialize(new CommandColumnDto[] {
|
||||
new() {
|
||||
|
|
@ -1638,8 +1639,8 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
DisplayExpr = "name",
|
||||
ValueExpr = "key",
|
||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||
new () { Key="Aktif", Name="Aktif" },
|
||||
new () { Key="Pasif", Name="Pasif" },
|
||||
new () { Key= "active", Name= "Aktif" },
|
||||
new () { Key= "passive", Name= "Pasif" },
|
||||
}),
|
||||
}),
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
|
|
@ -3044,7 +3045,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
ValueExpr = "key",
|
||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||
new () { Key= "active", Name= "Aktif" },
|
||||
new () { Key= "passive", Name= "Kapalı" },
|
||||
new () { Key= "passive", Name= "Pasif" },
|
||||
}),
|
||||
}),
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public static class ListFormSeeder_DefaultJsons
|
|||
{
|
||||
public static string DefaultDeleteCommand(string tableName)
|
||||
{
|
||||
return $"UPDATE \"{TableNameResolver.GetFullTableName(tableName)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";
|
||||
return $"UPDATE \"{TableNameResolver.GetFullTableName(tableName)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id";
|
||||
}
|
||||
|
||||
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid, string newId = "@NEWID") => JsonSerializer.Serialize(new FieldsDefaultValue[]
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
new EditingFormItemDto { Order=7, DataField = "PhoneNumber", ColSpan=1, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions=EditorOptionValues.PhoneEditorOptions },
|
||||
new EditingFormItemDto { Order=8, DataField = "FaxNumber", ColSpan=1, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions=EditorOptionValues.PhoneEditorOptions },
|
||||
new EditingFormItemDto { Order=9, DataField = "IsActive", ColSpan=1, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
|
||||
new EditingFormItemDto { Order=10, DataField = "MaxConcurrentUsers", ColSpan=1, IsRequired=false, EditorType2=EditorTypes.dxNumberBox },
|
||||
]
|
||||
},
|
||||
new() { Order=2, ColCount=3, ColSpan=1, ItemType="group", Items =
|
||||
|
|
@ -119,6 +118,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
new EditingFormItemDto { Order=8, DataField = "Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order=9, DataField = "Website", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order=10, DataField = "MenuGroup", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order=11, DataField = "MaxConcurrentUsers", ColSpan=1, IsRequired=false, EditorType2=EditorTypes.dxNumberBox },
|
||||
]
|
||||
}
|
||||
}),
|
||||
|
|
@ -2302,7 +2302,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
||||
PagerOptionJson = DefaultPagerOptionJson,
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 300, true, true, true, false, false),
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 550, true, true, true, false, false),
|
||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||
new() {
|
||||
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=[
|
||||
|
|
@ -4025,7 +4025,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
HeaderFilterJson = DefaultHeaderFilterJson,
|
||||
SearchPanelJson = DefaultSearchPanelJson,
|
||||
GroupPanelJson = DefaultGroupPanelJson,
|
||||
SelectionJson = DefaultSelectionMultipleJson,
|
||||
SelectionJson = DefaultSelectionSingleJson,
|
||||
ColumnOptionJson = DefaultColumnOptionJson(false),
|
||||
PermissionJson = DefaultPermissionJson(listFormName),
|
||||
PagerOptionJson = DefaultPagerOptionJson,
|
||||
|
|
@ -5469,7 +5469,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
DeleteCommand = $"UPDATE \"{FullNameTable(TableNameEnum.Route)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||
PagerOptionJson = DefaultPagerOptionJson,
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 400, true, true, true, true, false),
|
||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 550, true, true, true, true, false),
|
||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
|
||||
{
|
||||
new() {
|
||||
|
|
@ -5477,9 +5477,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
[
|
||||
new EditingFormItemDto { Order = 1, DataField = "Key", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 2, DataField = "Path", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 3, DataField = "ComponentPath", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 4, DataField = "RouteType", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 5, DataField = "Authority", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }
|
||||
new EditingFormItemDto { Order = 3, DataField = "ComponentType", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
|
||||
new EditingFormItemDto { Order = 4, DataField = "ComponentPath", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 5, DataField = "RouteType", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 6, DataField = "Authority", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }
|
||||
]
|
||||
}
|
||||
}),
|
||||
|
|
@ -5541,6 +5542,33 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "ComponentType",
|
||||
CaptionName = "App.Listform.ListformField.ComponentType",
|
||||
Width = 0,
|
||||
ListOrderNo = 5,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = false,
|
||||
LookupJson = JsonSerializer.Serialize(new LookupDto
|
||||
{
|
||||
DataSourceType = UiLookupDataSourceTypeEnum.StaticData,
|
||||
DisplayExpr = "name",
|
||||
ValueExpr = "key",
|
||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||
new () { Key="normal",Name="Normal" },
|
||||
new () { Key="dynamic",Name="Dynamic" },
|
||||
}),
|
||||
}),
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
|
|
@ -5548,7 +5576,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "ComponentPath",
|
||||
CaptionName = "App.Listform.ListformField.ComponentPath",
|
||||
Width = 0,
|
||||
ListOrderNo = 4,
|
||||
ListOrderNo = 5,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -5565,7 +5593,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "RouteType",
|
||||
CaptionName = "App.Listform.ListformField.RouteType",
|
||||
Width = 0,
|
||||
ListOrderNo = 5,
|
||||
ListOrderNo = 6,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -5593,7 +5621,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Authority",
|
||||
CaptionName = "App.Listform.ListformField.Authority",
|
||||
Width = 0,
|
||||
ListOrderNo = 6,
|
||||
ListOrderNo = 7,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class RouteSeedDto
|
|||
{
|
||||
public string Key { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string ComponentType { get; set; }
|
||||
public string ComponentPath { get; set; }
|
||||
public string RouteType { get; set; }
|
||||
public string[] Authority { get; set; }
|
||||
|
|
@ -79,6 +80,7 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
await _routeRepository.InsertAsync(new Route(
|
||||
item.Key,
|
||||
item.Path,
|
||||
item.ComponentType,
|
||||
item.ComponentPath,
|
||||
item.RouteType,
|
||||
item.Authority ?? []
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
{
|
||||
"Routes": [
|
||||
{
|
||||
"key": "dynamic.RoleListComponent",
|
||||
"key": "roleListComponent",
|
||||
"path": "/admin/RoleListComponent",
|
||||
"componentPath": "dynamic:RoleListComponent",
|
||||
"componentType": "dynamic",
|
||||
"componentPath": "RoleListComponent",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
},
|
||||
{
|
||||
"key": "home",
|
||||
"path": "/home",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Home",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -17,6 +19,7 @@
|
|||
{
|
||||
"key": "about",
|
||||
"path": "/about",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/About",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -24,6 +27,7 @@
|
|||
{
|
||||
"key": "products",
|
||||
"path": "/products",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Products",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -31,6 +35,7 @@
|
|||
{
|
||||
"key": "checkout",
|
||||
"path": "/checkout",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Checkout",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -38,6 +43,7 @@
|
|||
{
|
||||
"key": "payment",
|
||||
"path": "/payment",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Payment",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -45,6 +51,7 @@
|
|||
{
|
||||
"key": "success",
|
||||
"path": "/success",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Success",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -52,6 +59,7 @@
|
|||
{
|
||||
"key": "services",
|
||||
"path": "/services",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Services",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -59,6 +67,7 @@
|
|||
{
|
||||
"key": "blog",
|
||||
"path": "/blog",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Blog",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -66,6 +75,7 @@
|
|||
{
|
||||
"key": "blogDetail",
|
||||
"path": "/blog/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/BlogDetail",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -73,6 +83,7 @@
|
|||
{
|
||||
"key": "demo",
|
||||
"path": "/demo",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Demo",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -80,6 +91,7 @@
|
|||
{
|
||||
"key": "contact",
|
||||
"path": "/contact",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Contact",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -87,6 +99,7 @@
|
|||
{
|
||||
"key": "access-denied",
|
||||
"path": "/access-denied",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/AccessDenied",
|
||||
"routeType": "public",
|
||||
"authority": []
|
||||
|
|
@ -94,6 +107,7 @@
|
|||
{
|
||||
"key": "login",
|
||||
"path": "/login",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/Login",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -101,6 +115,7 @@
|
|||
{
|
||||
"key": "register",
|
||||
"path": "/register",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/Register",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -108,6 +123,7 @@
|
|||
{
|
||||
"key": "forgotPassword",
|
||||
"path": "/forgot-password",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/ForgotPassword",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -115,6 +131,7 @@
|
|||
{
|
||||
"key": "resetPassword",
|
||||
"path": "/reset-password",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/ResetPassword",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -122,6 +139,7 @@
|
|||
{
|
||||
"key": "sendConfirmationCode",
|
||||
"path": "/confirm",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/SendConfirmationCode",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -129,6 +147,7 @@
|
|||
{
|
||||
"key": "sendExtendLogin",
|
||||
"path": "/extend-login",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/ExtendLogin",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -136,6 +155,7 @@
|
|||
{
|
||||
"key": "verifyConfirmationCode",
|
||||
"path": "/confirm/:userId/:token",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/auth/VerifyConfirmationCode",
|
||||
"routeType": "authenticated",
|
||||
"authority": []
|
||||
|
|
@ -143,6 +163,7 @@
|
|||
{
|
||||
"key": "admin.dashboard",
|
||||
"path": "/admin/dashboard",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/Dashboard",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -150,6 +171,7 @@
|
|||
{
|
||||
"key": "admin.menuManager",
|
||||
"path": "/admin/menuManager",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/menu/MenuManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Menus.Manager"]
|
||||
|
|
@ -157,6 +179,7 @@
|
|||
{
|
||||
"key": "admin.listFormManagement.wizard",
|
||||
"path": "/admin/listform/wizard",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/listForm/wizard/Wizard",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Listforms.Wizard"]
|
||||
|
|
@ -164,6 +187,7 @@
|
|||
{
|
||||
"key": "admin.listFormManagement.wizardManager",
|
||||
"path": "/admin/listform/wizardManager",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/listForm/wizard/WizardFileManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Listforms.Wizard"]
|
||||
|
|
@ -171,6 +195,7 @@
|
|||
{
|
||||
"key": "admin.listFormManagement.edit",
|
||||
"path": "/admin/listform/edit/:listFormCode",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/listForm/edit/FormEdit",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -178,6 +203,7 @@
|
|||
{
|
||||
"key": "admin.forumManagement",
|
||||
"path": "/admin/forumManagement",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/forum/Management",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.ForumManagement"]
|
||||
|
|
@ -185,6 +211,7 @@
|
|||
{
|
||||
"key": "admin.ai",
|
||||
"path": "/admin/ai",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/ai/Assistant",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Definitions.AiBot.Asistant"]
|
||||
|
|
@ -192,6 +219,7 @@
|
|||
{
|
||||
"key": "admin.profile.general",
|
||||
"path": "/admin/profile/general",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/profile/Profile",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -199,6 +227,7 @@
|
|||
{
|
||||
"key": "admin.profile.password",
|
||||
"path": "/admin/profile/password",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/profile/Profile",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -206,6 +235,7 @@
|
|||
{
|
||||
"key": "admin.profile.notificationSettings",
|
||||
"path": "/admin/profile/notification-settings",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/profile/Profile",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -213,6 +243,7 @@
|
|||
{
|
||||
"key": "admin.activityLog",
|
||||
"path": "/admin/activityLog",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/activityLog/ActivityLog",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -220,6 +251,7 @@
|
|||
{
|
||||
"key": "admin.changeLog",
|
||||
"path": "/admin/changeLog",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/version/ChangeLog",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -227,6 +259,7 @@
|
|||
{
|
||||
"key": "admin.settings",
|
||||
"path": "/admin/settings",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/settings/Settings",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Setting"]
|
||||
|
|
@ -234,6 +267,7 @@
|
|||
{
|
||||
"key": "admin.identity.user.detail",
|
||||
"path": "/admin/users/detail/:userId",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/user-management/Details",
|
||||
"routeType": "protected",
|
||||
"authority": ["AbpIdentity.Users.Update"]
|
||||
|
|
@ -241,6 +275,7 @@
|
|||
{
|
||||
"key": "admin.identity.ous",
|
||||
"path": "/admin/ous",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
||||
"routeType": "protected",
|
||||
"authority": ["Abp.Identity.OrganizationUnits"]
|
||||
|
|
@ -248,6 +283,7 @@
|
|||
{
|
||||
"key": "admin.hr.organization",
|
||||
"path": "/admin/organization",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/hr/OrgChart",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Definitions.Department"]
|
||||
|
|
@ -255,6 +291,7 @@
|
|||
{
|
||||
"key": "admin.forum",
|
||||
"path": "/admin/forum",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/forum/Forum",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.ForumManagement.Publish"]
|
||||
|
|
@ -262,6 +299,7 @@
|
|||
{
|
||||
"key": "admin.list",
|
||||
"path": "/admin/list/:listFormCode",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/list/List",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -269,6 +307,7 @@
|
|||
{
|
||||
"key": "admin.formNew",
|
||||
"path": "/admin/form/:listFormCode",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/form/FormNew",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -276,6 +315,7 @@
|
|||
{
|
||||
"key": "admin.formView",
|
||||
"path": "/admin/form/:listFormCode/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/form/FormView",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -283,6 +323,7 @@
|
|||
{
|
||||
"key": "admin.formEdit",
|
||||
"path": "/admin/form/:listFormCode/:id/edit",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/form/FormEdit",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -290,6 +331,7 @@
|
|||
{
|
||||
"key": "admin.chart",
|
||||
"path": "/admin/chart/:listFormCode",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/list/Chart",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -297,6 +339,7 @@
|
|||
{
|
||||
"key": "admin.sqlQueryManager",
|
||||
"path": "/admin/sqlQueryManager",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/SqlQueryManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.SqlQueryManager"]
|
||||
|
|
@ -304,6 +347,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.endpoints",
|
||||
"path": "/admin/developerkit/endpoints",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/CrudEndpointManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.CrudEndpoints"]
|
||||
|
|
@ -311,6 +355,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.dynamic-services",
|
||||
"path": "/admin/developerkit/dynamic-services",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/DynamicServiceManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.DynamicServices"]
|
||||
|
|
@ -318,6 +363,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.dynamic-services.new",
|
||||
"path": "/admin/developerkit/dynamic-services/new",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/DynamicServiceEditor",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.DynamicServices"]
|
||||
|
|
@ -325,6 +371,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.dynamic-services.edit",
|
||||
"path": "/admin/developerkit/dynamic-services/edit/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/DynamicServiceEditor",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.DynamicServices"]
|
||||
|
|
@ -332,6 +379,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.components",
|
||||
"path": "/admin/developerkit/components",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/ComponentManagerPage",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.Components"]
|
||||
|
|
@ -339,6 +387,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.components.new",
|
||||
"path": "/admin/developerkit/components/new",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/ComponentEditorPage",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.Components"]
|
||||
|
|
@ -346,6 +395,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.components.view",
|
||||
"path": "/admin/developerkit/components/view/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/ComponentEditorPage",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.Components"]
|
||||
|
|
@ -353,6 +403,7 @@
|
|||
{
|
||||
"key": "admin.developerkit.components.edit",
|
||||
"path": "/admin/developerkit/components/edit/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/developerKit/ComponentCodeLayout",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.DeveloperKit.Components"]
|
||||
|
|
@ -360,6 +411,7 @@
|
|||
{
|
||||
"key": "admin.fileManagement",
|
||||
"path": "/admin/files",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/files/FileManager",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Files"]
|
||||
|
|
@ -367,6 +419,7 @@
|
|||
{
|
||||
"key": "admin.devexpressReportView",
|
||||
"path": "/admin/reports/:id/view",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/report/DevexpressReportViewer",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -374,6 +427,7 @@
|
|||
{
|
||||
"key": "admin.devexpressReportDesigner",
|
||||
"path": "/admin/reports/:id/design",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/report/DevexpressReportDesigner",
|
||||
"routeType": "protected",
|
||||
"authority": []
|
||||
|
|
@ -381,6 +435,7 @@
|
|||
{
|
||||
"key": "homeDesigner",
|
||||
"path": "/admin/public/home/designer",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Home",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Home"]
|
||||
|
|
@ -388,6 +443,7 @@
|
|||
{
|
||||
"key": "aboutDesigner",
|
||||
"path": "/admin/public/about/designer",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/About",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.About"]
|
||||
|
|
@ -395,6 +451,7 @@
|
|||
{
|
||||
"key": "servicesDesigner",
|
||||
"path": "/admin/public/services/designer",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Services",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Services"]
|
||||
|
|
@ -402,6 +459,7 @@
|
|||
{
|
||||
"key": "contactDesigner",
|
||||
"path": "/admin/public/contact/designer",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/public/Contact",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Contact"]
|
||||
|
|
@ -409,6 +467,7 @@
|
|||
{
|
||||
"key": "admin.videoroom.dashboard",
|
||||
"path": "/admin/videoroom/dashboard",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/videoroom/Dashboard",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Videoroom.Dashboard"]
|
||||
|
|
@ -416,6 +475,7 @@
|
|||
{
|
||||
"key": "admin.videoroom.list",
|
||||
"path": "/admin/videoroom/list",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/videoroom/RoomList",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Videoroom.List"]
|
||||
|
|
@ -423,6 +483,7 @@
|
|||
{
|
||||
"key": "admin.videoroom.roomdetail",
|
||||
"path": "/admin/videoroom/room/:id",
|
||||
"componentType": "normal",
|
||||
"componentPath": "@/views/admin/videoroom/RoomDetail",
|
||||
"routeType": "protected",
|
||||
"authority": ["App.Videoroom.RoomDetail"]
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
|
|||
branchName = tenant.GetOrganizationName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Default Branch otomatik olarak oluşturuluyor.
|
||||
var defaultBranch = await _branchRepository.FirstOrDefaultAsync(b => b.Code == branchCode);
|
||||
if (defaultBranch == null)
|
||||
|
|
@ -135,10 +135,10 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
|
|||
Surname = PlatformConsts.AbpIdentity.User.AdminSurNameDefaultValue,
|
||||
};
|
||||
|
||||
adminUser.SetEmailConfirmed(true);
|
||||
adminUser.SetIsVerified(true);
|
||||
adminUser.SetEmailConfirmed(true);
|
||||
adminUser.SetRocketUsername(PlatformConsts.AbpIdentity.User.AdminRocketUsernameDefaultValue);
|
||||
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, true);
|
||||
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, adminUser.PhoneNumberConfirmed);
|
||||
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
|
||||
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
|
||||
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
IF OBJECT_ID(N'[dbo].[Sal_T_Approval]', 'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE [dbo].[Sal_T_Approval]
|
||||
(
|
||||
[Id] uniqueidentifier NOT NULL DEFAULT NEWID(),
|
||||
[CreationTime] datetime2 NOT NULL DEFAULT GETUTCDATE(),
|
||||
[CreatorId] uniqueidentifier NULL,
|
||||
[LastModificationTime] datetime2 NULL,
|
||||
[LastModifierId] uniqueidentifier NULL,
|
||||
[IsDeleted] bit NOT NULL DEFAULT 0,
|
||||
[DeletionTime] datetime2 NULL,
|
||||
[DeleterId] uniqueidentifier NULL,
|
||||
[TenantId] uniqueidentifier NULL,
|
||||
[ApprovalUserName] nvarchar(256) NULL,
|
||||
[ApprovalStatus] nvarchar(50) NULL,
|
||||
[ApprovalDate] datetime NULL,
|
||||
[ApprovalDescription] nvarchar(200) NULL,
|
||||
[Name] nvarchar(100) NULL,
|
||||
CONSTRAINT [PK_Sal_T_Approval] PRIMARY KEY NONCLUSTERED
|
||||
(
|
||||
[Id] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
END
|
||||
GO
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
{
|
||||
"Wizard": {
|
||||
"WizardName": "Approval",
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"MenuCode": "App.Wizard.Approval",
|
||||
"IsTenant": true,
|
||||
"IsBranch": false,
|
||||
"IsOrganizationUnit": false,
|
||||
"AllowAdding": true,
|
||||
"AllowUpdating": true,
|
||||
"AllowDeleting": true,
|
||||
"AllowDetail": false,
|
||||
"ConfirmDelete": true,
|
||||
"DefaultLayout": "grid",
|
||||
"Grid": true,
|
||||
"Pivot": true,
|
||||
"Tree": true,
|
||||
"Chart": true,
|
||||
"Gantt": true,
|
||||
"Scheduler": true,
|
||||
"LanguageTextMenuEn": "Approval",
|
||||
"LanguageTextMenuTr": "Approval",
|
||||
"LanguageTextTitleEn": "Approval",
|
||||
"LanguageTextTitleTr": "Onaylama",
|
||||
"LanguageTextDescEn": "Approval",
|
||||
"LanguageTextDescTr": "Onaylama",
|
||||
"LanguageTextMenuParentEn": "Sales",
|
||||
"LanguageTextMenuParentTr": "Sat\u0131\u015F",
|
||||
"PermissionGroupName": "App.Wizard.Sales",
|
||||
"MenuParentCode": "App.Wizard.Sales",
|
||||
"MenuParentIcon": "FcAssistant",
|
||||
"MenuIcon": "FcAndroidOs",
|
||||
"DataSourceCode": "Default",
|
||||
"DataSourceConnectionString": "",
|
||||
"SelectCommandType": 1,
|
||||
"SelectCommand": "Sal_T_Approval",
|
||||
"KeyFieldName": "Id",
|
||||
"KeyFieldDbSourceType": 9,
|
||||
"TreeKeyExpr": "",
|
||||
"TreeParentIdExpr": "",
|
||||
"TreeAutoExpandAll": false,
|
||||
"GanttKeyExpr": "",
|
||||
"GanttParentIdExpr": "",
|
||||
"GanttAutoExpandAll": false,
|
||||
"GanttTitleExpr": "",
|
||||
"GanttStartExpr": "",
|
||||
"GanttEndExpr": "",
|
||||
"GanttProgressExpr": "",
|
||||
"SchedulerTextExpr": "",
|
||||
"SchedulerStartDateExpr": "",
|
||||
"SchedulerEndDateExpr": "",
|
||||
"Groups": [
|
||||
{
|
||||
"Caption": "",
|
||||
"ColCount": 1,
|
||||
"Items": [
|
||||
{
|
||||
"DataField": "Id",
|
||||
"CaptionName": "App.Listform.ListformField.Id",
|
||||
"EditorType": "dxTextBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": true,
|
||||
"IncludeInEditingForm": true,
|
||||
"DbSourceType": 9,
|
||||
"TurkishCaption": "Id",
|
||||
"EnglishCaption": "Id",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
},
|
||||
{
|
||||
"DataField": "Name",
|
||||
"CaptionName": "App.Listform.ListformField.Name",
|
||||
"EditorType": "dxTextBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": false,
|
||||
"IncludeInEditingForm": true,
|
||||
"DbSourceType": 16,
|
||||
"TurkishCaption": "Name",
|
||||
"EnglishCaption": "Name",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
},
|
||||
{
|
||||
"DataField": "ApprovalUserName",
|
||||
"CaptionName": "App.Listform.ListformField.ApprovalUserName",
|
||||
"EditorType": "dxTextBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": false,
|
||||
"IncludeInEditingForm": false,
|
||||
"DbSourceType": 16,
|
||||
"TurkishCaption": "Approval User Name",
|
||||
"EnglishCaption": "Approval User Name",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
},
|
||||
{
|
||||
"DataField": "ApprovalStatus",
|
||||
"CaptionName": "App.Listform.ListformField.ApprovalStatus",
|
||||
"EditorType": "dxTextBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": false,
|
||||
"IncludeInEditingForm": false,
|
||||
"DbSourceType": 16,
|
||||
"TurkishCaption": "Approval Status",
|
||||
"EnglishCaption": "Approval Status",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
},
|
||||
{
|
||||
"DataField": "ApprovalDate",
|
||||
"CaptionName": "App.Listform.ListformField.ApprovalDate",
|
||||
"EditorType": "dxDateBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": false,
|
||||
"IncludeInEditingForm": false,
|
||||
"DbSourceType": 6,
|
||||
"TurkishCaption": "Approval Date",
|
||||
"EnglishCaption": "Approval Date",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
},
|
||||
{
|
||||
"DataField": "ApprovalDescription",
|
||||
"CaptionName": "App.Listform.ListformField.ApprovalDescription",
|
||||
"EditorType": "dxTextBox",
|
||||
"EditorOptions": "",
|
||||
"EditorScript": "",
|
||||
"ColSpan": 1,
|
||||
"IsRequired": false,
|
||||
"IncludeInEditingForm": false,
|
||||
"DbSourceType": 16,
|
||||
"TurkishCaption": "Approval Description",
|
||||
"EnglishCaption": "Approval Description",
|
||||
"LookupDataSourceType": 1,
|
||||
"ValueExpr": "Key",
|
||||
"DisplayExpr": "Name",
|
||||
"LookupQuery": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SubForms": [],
|
||||
"Widgets": [],
|
||||
"Workflow": {
|
||||
"ApprovalUserFieldName": "ApprovalUserName",
|
||||
"IsFilterUserName": true,
|
||||
"ApprovalDateFieldName": "ApprovalDate",
|
||||
"ApprovalStatusFieldName": "ApprovalStatus",
|
||||
"ApprovalDescriptionFieldName": "ApprovalDescription",
|
||||
"Criteria": [
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Start",
|
||||
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Ba\u015Flat1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "",
|
||||
"NextOnStart": "N002",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 34,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N001"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Approval",
|
||||
"Title": "Onay1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "admin@sozsoft.com",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "N003",
|
||||
"NextOnReject": "N004",
|
||||
"PositionX": 323,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N002"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Approval",
|
||||
"Title": "Onay2",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "demo@sozsoft.com",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "N004",
|
||||
"NextOnReject": "N004",
|
||||
"PositionX": 586,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N003"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Inform",
|
||||
"Title": "Bilgilendirme1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "system@sozsoft.com",
|
||||
"NextOnStart": "N005",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 458,
|
||||
"PositionY": 411,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N004"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "End",
|
||||
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Bitir1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 792,
|
||||
"PositionY": 412,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N005"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WorkflowCriteria": [
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Start",
|
||||
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Ba\u015Flat1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "",
|
||||
"NextOnStart": "N002",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 34,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N001"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Approval",
|
||||
"Title": "Onay1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "admin@sozsoft.com",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "N003",
|
||||
"NextOnReject": "N004",
|
||||
"PositionX": 323,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N002"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Approval",
|
||||
"Title": "Onay2",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "demo@sozsoft.com",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "N004",
|
||||
"NextOnReject": "N004",
|
||||
"PositionX": 586,
|
||||
"PositionY": 104,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N003"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "Inform",
|
||||
"Title": "Bilgilendirme1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "system@sozsoft.com",
|
||||
"NextOnStart": "N005",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 458,
|
||||
"PositionY": 411,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N004"
|
||||
},
|
||||
{
|
||||
"ListFormCode": "App.Wizard.Approval",
|
||||
"Kind": "End",
|
||||
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Bitir1",
|
||||
"CompareColumn": "Price",
|
||||
"CompareOperator": "\u003E",
|
||||
"CompareValue": 5000,
|
||||
"Approver": "",
|
||||
"NextOnStart": "",
|
||||
"NextOnTrue": "",
|
||||
"NextOnFalse": "",
|
||||
"NextOnApprove": "",
|
||||
"NextOnReject": "",
|
||||
"PositionX": 792,
|
||||
"PositionY": 412,
|
||||
"CompareOutcomes": [],
|
||||
"Id": "N005"
|
||||
}
|
||||
]
|
||||
},
|
||||
"IsDeletedField": true,
|
||||
"IsCreatedField": true,
|
||||
"InsertedRecords": {
|
||||
"LanguageKeys": [
|
||||
"App.Wizard.Approval",
|
||||
"App.Wizard.Approval.Title",
|
||||
"App.Wizard.Approval.Desc",
|
||||
"App.Listform.ListformField.ApprovalUserName",
|
||||
"App.Listform.ListformField.ApprovalStatus",
|
||||
"App.Listform.ListformField.ApprovalDate",
|
||||
"App.Listform.ListformField.ApprovalDescription"
|
||||
],
|
||||
"PermissionGroupNames": [
|
||||
"App.Wizard.Sales"
|
||||
],
|
||||
"PermissionNames": [
|
||||
"App.Wizard.Approval",
|
||||
"App.Wizard.Approval.Create",
|
||||
"App.Wizard.Approval.Update",
|
||||
"App.Wizard.Approval.Delete",
|
||||
"App.Wizard.Approval.Export",
|
||||
"App.Wizard.Approval.Import",
|
||||
"App.Wizard.Approval.Note"
|
||||
],
|
||||
"MenuCodes": [
|
||||
"App.Wizard.Approval"
|
||||
],
|
||||
"DataSourceCodes": []
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
||||
private readonly IRepository<ListForm, Guid> _repoListForm;
|
||||
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
||||
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
|
||||
private readonly ILogger<WizardDataSeeder> _logger;
|
||||
|
||||
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
||||
|
|
@ -51,6 +52,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
IRepository<DataSource, Guid> repoDataSource,
|
||||
IRepository<ListForm, Guid> repoListForm,
|
||||
IRepository<ListFormField, Guid> repoListFormField,
|
||||
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
|
||||
ILogger<WizardDataSeeder> logger)
|
||||
{
|
||||
_repoLangKey = repoLangKey;
|
||||
|
|
@ -62,6 +64,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
_repoDataSource = repoDataSource;
|
||||
_repoListForm = repoListForm;
|
||||
_repoListFormField = repoListFormField;
|
||||
_repoListFormWorkflow = repoListFormWorkflow;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -134,13 +137,29 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
input.Widgets ??= new List<WidgetEditDto>();
|
||||
input.Workflow ??= new WorkflowDto();
|
||||
input.WorkflowCriteria ??= new List<ListFormWorkflowCriteriaDto>();
|
||||
if (input.WorkflowCriteria.Count == 0 && input.Workflow.Criteria?.Count > 0)
|
||||
{
|
||||
input.WorkflowCriteria = input.Workflow.Criteria;
|
||||
}
|
||||
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
||||
|
||||
var wizardName = input.WizardName.Trim();
|
||||
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
|
||||
var nameLangKey = WizardConsts.WizardKey(wizardName);
|
||||
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
|
||||
var code = WizardConsts.WizardKey(wizardName);
|
||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
||||
? WizardConsts.WizardKey(wizardName)
|
||||
: input.MenuCode.Trim();
|
||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
||||
? code
|
||||
: input.ListFormCode.Trim();
|
||||
var titleLangKey = $"{listFormCode}.Title";
|
||||
var nameLangKey = code;
|
||||
var descLangKey = $"{listFormCode}.Desc";
|
||||
var permCreateName = $"{code}.Create";
|
||||
var permUpdateName = $"{code}.Update";
|
||||
var permDeleteName = $"{code}.Delete";
|
||||
var permExportName = $"{code}.Export";
|
||||
var permImportName = $"{code}.Import";
|
||||
var permNoteName = $"{code}.Note";
|
||||
|
||||
// Dil - Language Keys
|
||||
await CreateLangKeyAsync(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
|
||||
|
|
@ -153,7 +172,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
{
|
||||
await _repoPermGroup.InsertAsync(
|
||||
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
|
||||
await CreateLangKeyAsync(groupName, groupName, groupName);
|
||||
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
|
||||
await EnsureLangKeyAsync(groupName);
|
||||
else
|
||||
await CreateLangKeyAsync(groupName, groupName, groupName);
|
||||
}
|
||||
|
||||
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
|
||||
|
|
@ -165,35 +187,35 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
permRead = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permCreate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermCreate(wizardName));
|
||||
var permCreate = existingPerms.FirstOrDefault(a => a.Name == permCreateName);
|
||||
if (permCreate == null)
|
||||
permCreate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermCreate(wizardName), permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermUpdate(wizardName));
|
||||
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == permUpdateName);
|
||||
if (permUpdate == null)
|
||||
permUpdate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermUpdate(wizardName), permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permDelete = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermDelete(wizardName));
|
||||
var permDelete = existingPerms.FirstOrDefault(a => a.Name == permDeleteName);
|
||||
if (permDelete == null)
|
||||
permDelete = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermDelete(wizardName), permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permExport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermExport(wizardName));
|
||||
var permExport = existingPerms.FirstOrDefault(a => a.Name == permExportName);
|
||||
if (permExport == null)
|
||||
permExport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermExport(wizardName), permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permImport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermImport(wizardName));
|
||||
var permImport = existingPerms.FirstOrDefault(a => a.Name == permImportName);
|
||||
if (permImport == null)
|
||||
permImport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermImport(wizardName), permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
var permNote = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermNote(wizardName));
|
||||
var permNote = existingPerms.FirstOrDefault(a => a.Name == permNoteName);
|
||||
if (permNote == null)
|
||||
permNote = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||
Guid.NewGuid(), groupName, WizardConsts.PermNote(wizardName), permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: true);
|
||||
Guid.NewGuid(), groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: true);
|
||||
|
||||
// // Permission Grants - Admin role için, sadece eksik olanları ekle
|
||||
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
|
||||
|
|
@ -219,12 +241,18 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
if (menuParent == null)
|
||||
{
|
||||
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
||||
await CreateLangKeyAsync(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
|
||||
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 = WizardConsts.WizardKeyParent(wizardName),
|
||||
DisplayName = input.MenuParentCode,
|
||||
IsDisabled = false,
|
||||
Icon = menuParentIcon,
|
||||
Order = maxRootOrder + 1,
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
|
@ -276,7 +304,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
ColSpan = g.ColCount,
|
||||
ItemType = "group",
|
||||
Items = g.Items
|
||||
.Where(i => i.DataField != input.KeyFieldName)
|
||||
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
|
||||
.Select((it, ii) => new EditingFormItemDto
|
||||
{
|
||||
Order = ii + 1,
|
||||
|
|
@ -289,6 +317,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
})
|
||||
.ToArray()
|
||||
})
|
||||
.Where(g => g.Items.Length > 0)
|
||||
.ToList();
|
||||
|
||||
// ListForm - varsa sil, yeniden ekle
|
||||
|
|
@ -304,6 +333,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
|
||||
}
|
||||
|
||||
var existingWorkflowCriteria = await _repoListFormWorkflow.GetListAsync(a => a.ListFormCode == input.ListFormCode);
|
||||
if (existingWorkflowCriteria.Count > 0)
|
||||
{
|
||||
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
||||
}
|
||||
|
||||
// ListForm
|
||||
await _repoListForm.InsertAsync(new ListForm
|
||||
{
|
||||
|
|
@ -311,7 +346,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
PageSize = 10,
|
||||
ExportJson = WizardConsts.DefaultExportJson,
|
||||
IsSubForm = false,
|
||||
ShowNote = input.SubForms.Count > 0,
|
||||
ShowNote = input.SubForms.Count > 0 || input.WorkflowCriteria.Count > 0,
|
||||
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler),
|
||||
CultureName = LanguageCodes.En,
|
||||
ListFormCode = input.ListFormCode,
|
||||
|
|
@ -332,7 +367,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
||||
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson,
|
||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
|
||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
||||
|
|
@ -381,6 +416,34 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var criteria in input.WorkflowCriteria)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(criteria.Id))
|
||||
{
|
||||
_logger.LogWarning("Workflow criteria skipped because Id is empty. ListFormCode: {ListFormCode}, Title: {Title}", input.ListFormCode, criteria.Title);
|
||||
continue;
|
||||
}
|
||||
|
||||
await _repoListFormWorkflow.InsertAsync(new ListFormWorkflow(criteria.Id)
|
||||
{
|
||||
ListFormCode = string.IsNullOrWhiteSpace(criteria.ListFormCode) ? input.ListFormCode : criteria.ListFormCode,
|
||||
Kind = criteria.Kind,
|
||||
Title = criteria.Title,
|
||||
CompareColumn = criteria.CompareColumn,
|
||||
CompareOperator = criteria.CompareOperator,
|
||||
CompareValue = criteria.CompareValue,
|
||||
Approver = criteria.Approver,
|
||||
NextOnStart = criteria.NextOnStart,
|
||||
NextOnTrue = criteria.NextOnTrue,
|
||||
NextOnFalse = criteria.NextOnFalse,
|
||||
NextOnApprove = criteria.NextOnApprove,
|
||||
NextOnReject = criteria.NextOnReject,
|
||||
PositionX = criteria.PositionX,
|
||||
PositionY = criteria.PositionY,
|
||||
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
|
||||
}, autoSave: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasWorkflow(WorkflowDto workflow, List<ListFormWorkflowCriteriaDto> criteria)
|
||||
|
|
@ -394,6 +457,60 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
);
|
||||
}
|
||||
|
||||
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
||||
{
|
||||
if (criteria == null || criteria.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var baseTitles = criteria
|
||||
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
||||
.ToList();
|
||||
var duplicateTitles = baseTitles
|
||||
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.Where(x => x.Count() > 1)
|
||||
.Select(x => x.Key)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var item in criteria)
|
||||
{
|
||||
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
||||
var title = baseTitle;
|
||||
|
||||
if (duplicateTitles.Contains(baseTitle))
|
||||
{
|
||||
titleCounts.TryGetValue(baseTitle, out var count);
|
||||
count++;
|
||||
titleCounts[baseTitle] = count;
|
||||
title = $"{baseTitle}{count}";
|
||||
}
|
||||
|
||||
if (usedTitles.Contains(title))
|
||||
{
|
||||
var index = 1;
|
||||
var candidate = $"{title}{index}";
|
||||
while (usedTitles.Contains(candidate))
|
||||
{
|
||||
index++;
|
||||
candidate = $"{title}{index}";
|
||||
}
|
||||
|
||||
title = candidate;
|
||||
}
|
||||
|
||||
item.Title = title;
|
||||
usedTitles.Add(title);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeWorkflowTitleFallback(string kind)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
||||
}
|
||||
|
||||
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)) return;
|
||||
|
|
@ -427,6 +544,16 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
}, autoSave: true);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnsureLangKeyAsync(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)) return;
|
||||
|
||||
if (!await _repoLangKey.AnyAsync(a => a.ResourceName == _appName && a.Key == key))
|
||||
{
|
||||
await _repoLangKey.InsertAsync(new LanguageKey { ResourceName = _appName, Key = key }, autoSave: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.21" />
|
||||
<PackageReference Include="Volo.Abp.Autofac" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="10.0.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" />
|
||||
<ProjectReference Include="..\Sozsoft.Platform.Application.Contracts\Sozsoft.Platform.Application.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Sozsoft.Platform.EntityFrameworkCore\Sozsoft.Platform.EntityFrameworkCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -93,6 +93,13 @@ public static class PlatformConsts
|
|||
{
|
||||
public const string GroupName = $"{Prefix.Abp}.Identity";
|
||||
|
||||
public static class SignIn
|
||||
{
|
||||
public const string Default = GroupName + ".SignIn";
|
||||
public const string RequireConfirmedEmail = Default + ".RequireConfirmedEmail";
|
||||
public const string RequireConfirmedPhoneNumber = Default + ".RequireConfirmedPhoneNumber";
|
||||
}
|
||||
|
||||
public static class Profile
|
||||
{
|
||||
public const string Default = GroupName + ".Profile";
|
||||
|
|
@ -162,7 +169,7 @@ public static class PlatformConsts
|
|||
public const string AdminRoleName = "admin";
|
||||
public const string AdminNameDefaultValue = "Sedat";
|
||||
public const string AdminSurNameDefaultValue = "ÖZTÜRK";
|
||||
public const string AdminEmailDefaultValue = "SYSTEM@SOZSOFT.COM";
|
||||
public const string AdminEmailDefaultValue = "system@sozsoft.com";
|
||||
public const string AdminPasswordDefaultValue = "1q2w3E*";
|
||||
public const string AdminPhoneNumberDefaultValue = "05449476346";
|
||||
public const string AdminRocketUsernameDefaultValue = "sedat.ozturk";
|
||||
|
|
@ -766,10 +773,10 @@ public static class PlatformConsts
|
|||
{
|
||||
public static class ParameterTypes
|
||||
{
|
||||
public const string Static = "S";
|
||||
public const string Query = "Q";
|
||||
public const string Path = "P";
|
||||
public const string Body = "B";
|
||||
public const string Static = "Static";
|
||||
public const string Query = "Query";
|
||||
public const string Path = "Path";
|
||||
public const string Body = "Body";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ public static class WizardConsts
|
|||
public static readonly string DefaultSearchPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
||||
public static readonly string DefaultGroupPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
||||
|
||||
public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new
|
||||
public static string DefaultSelectionSingleJson(string Mode = "none") => JsonSerializer.Serialize(new
|
||||
{
|
||||
Mode = GridOptions.SelectionModeNone,
|
||||
Mode = Mode,
|
||||
AllowSelectAll = false
|
||||
});
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ public static class WizardConsts
|
|||
R = permissionName,
|
||||
U = permissionName + ".Update",
|
||||
E = true,
|
||||
I = false,
|
||||
I = true,
|
||||
Deny = false
|
||||
});
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ public static class WizardConsts
|
|||
|
||||
public static string DefaultDeleteCommand(string tableName)
|
||||
{
|
||||
return $"UPDATE \"{tableName}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";
|
||||
return $"UPDATE \"{tableName}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id";
|
||||
}
|
||||
|
||||
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class PlatformBackgroundWorkerTemplateDefinitionProvider : TemplateDefini
|
|||
foreach (var worker in workers.Where(a =>
|
||||
a.IsActive &&
|
||||
a.WorkerType == WorkerTypeEnum.MailQueueWorker &&
|
||||
a.Options != null && a.Options != string.Empty).ToList())
|
||||
a.Options != null).ToList().Where(a => !a.Options.IsNullOrWhiteSpace()))
|
||||
{
|
||||
var Options = JsonSerializer.Deserialize<MailQueueWorkerOptions>(worker.Options);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ public class Route : FullAuditedEntity<Guid>
|
|||
{
|
||||
public string Key { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string ComponentType { get; set; }
|
||||
public string ComponentPath { get; set; }
|
||||
public string RouteType { get; set; }
|
||||
public string[] Authority { get; set; }
|
||||
|
||||
public Route(string key, string path, string componentPath, string routeType, string[] authority)
|
||||
public Route(string key, string path, string componentType, string componentPath, string routeType, string[] authority)
|
||||
{
|
||||
Key = key;
|
||||
Path = path;
|
||||
ComponentType = componentType;
|
||||
ComponentPath = componentPath;
|
||||
RouteType = routeType;
|
||||
Authority = authority;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ namespace Sozsoft.Platform.Entities;
|
|||
|
||||
public class Note : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Note()
|
||||
{
|
||||
}
|
||||
|
||||
public Note(Guid id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid? TenantId { get; set; }
|
||||
public string EntityName { get; set; }
|
||||
public string EntityId { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Flurl.Http;
|
||||
using Sozsoft.Platform.Localization;
|
||||
|
|
@ -23,6 +24,11 @@ public class TurnstileCaptchaManager : PlatformDomainService, ICaptchaManager
|
|||
|
||||
public async Task<bool> VerifyCaptchaAsync(string CaptchaResponse, bool throwOnFailure = false)
|
||||
{
|
||||
if (CaptchaResponse.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var endPoint = await SettingProvider.GetOrNullAsync(PlatformConsts.AbpAccount.Captcha.EndPoint);
|
||||
var privateKey = await SettingProvider.GetOrNullAsync(PlatformConsts.AbpAccount.Captcha.SecretKey);
|
||||
if (endPoint.IsNullOrWhiteSpace() || privateKey.IsNullOrWhiteSpace())
|
||||
|
|
@ -46,18 +52,24 @@ public class TurnstileCaptchaManager : PlatformDomainService, ICaptchaManager
|
|||
|
||||
if (response != null && response.StatusCode == 200)
|
||||
{
|
||||
var result = await response.GetJsonAsync<dynamic>();
|
||||
if (!(bool)result.success)
|
||||
var result = await response.GetJsonAsync<TurnstileCaptchaVerifyResponse>();
|
||||
if (result?.Success != true)
|
||||
{
|
||||
if (throwOnFailure)
|
||||
{
|
||||
throw new UserFriendlyException(Localizer[PlatformConsts.AbpIdentity.User.CaptchaWrongCode]);
|
||||
}
|
||||
}
|
||||
return (bool)result.success;
|
||||
|
||||
return result?.Success == true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class TurnstileCaptchaVerifyResponse
|
||||
{
|
||||
[JsonPropertyName("success")]
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ public class DefaultValueManager : PlatformDomainService, IDefaultValueManager
|
|||
else if (defaultField.Value == PlatformConsts.DefaultValues.Year)
|
||||
value = Clock.Now.Year;
|
||||
else if (defaultField.Value == PlatformConsts.DefaultValues.Id)
|
||||
value = keys?.FirstOrDefault();
|
||||
value = op == OperationEnum.Delete
|
||||
? keys
|
||||
: keys?.FirstOrDefault();
|
||||
else if (defaultField.Value == PlatformConsts.DefaultValues.NewId)
|
||||
value = Guid.NewGuid();
|
||||
else if (defaultField.Value == PlatformConsts.DefaultValues.Selected_Ids)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Sozsoft.Platform.DynamicData;
|
||||
using Sozsoft.Platform.Entities;
|
||||
|
|
@ -169,7 +170,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
|||
// oncelik Command alanindadir, dolu ise silme islemi buradaki sorguya yonlendirilir
|
||||
if (!string.IsNullOrEmpty(command))
|
||||
{
|
||||
sql = command;
|
||||
sql = NormalizeCollectionParameterCommand(command, parameters, dataSourceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -189,7 +190,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
|||
{
|
||||
var where = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN (@{listForm.KeyFieldName})",
|
||||
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN @{listForm.KeyFieldName}",
|
||||
DataSourceTypeEnum.Postgresql => $"\"{listForm.KeyFieldName}\" = ANY(@{listForm.KeyFieldName})",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
|
@ -209,7 +210,14 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
|||
string where = string.Empty;
|
||||
if (parameters.Any())
|
||||
{
|
||||
where = string.Join(" AND ", parameters.Select(a => $"\"{a.Key}\" IN (@{a.Key})").ToList());
|
||||
where = string.Join(
|
||||
" AND ",
|
||||
parameters.Select(a => dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => $"\"{a.Key}\" IN @{a.Key}",
|
||||
DataSourceTypeEnum.Postgresql => $"\"{a.Key}\" = ANY(@{a.Key})",
|
||||
_ => "1 = 0",
|
||||
}).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -220,7 +228,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
|||
|
||||
where = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN (@{listForm.KeyFieldName})",
|
||||
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN @{listForm.KeyFieldName}",
|
||||
DataSourceTypeEnum.Postgresql => $"\"{listForm.KeyFieldName}\" = ANY(@{listForm.KeyFieldName})",
|
||||
_ => "1 = 0",
|
||||
};
|
||||
|
|
@ -231,5 +239,78 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
|||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static string NormalizeCollectionParameterCommand(
|
||||
string command,
|
||||
Dictionary<string, object> parameters,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command) || parameters == null || parameters.Count == 0)
|
||||
{
|
||||
return command;
|
||||
}
|
||||
|
||||
var sql = command;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (!IsCollectionParameter(parameter.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var escapedParameterName = Regex.Escape(parameter.Key);
|
||||
sql = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => NormalizeMssqlCollectionParameter(sql, escapedParameterName, parameter.Key),
|
||||
DataSourceTypeEnum.Postgresql => NormalizePostgresqlCollectionParameter(sql, escapedParameterName, parameter.Key),
|
||||
_ => sql
|
||||
};
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static bool IsCollectionParameter(object value)
|
||||
{
|
||||
return value is System.Collections.IEnumerable
|
||||
&& value is not string
|
||||
&& value is not byte[];
|
||||
}
|
||||
|
||||
private static string NormalizeMssqlCollectionParameter(
|
||||
string sql,
|
||||
string escapedParameterName,
|
||||
string parameterName)
|
||||
{
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
$@"IN\s*\(\s*@{escapedParameterName}\s*\)",
|
||||
$"IN @{parameterName}",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
return Regex.Replace(
|
||||
sql,
|
||||
$@"(?<column>(""[^""]+""|\[[^\]]+\]|`[^`]+`|\w+))\s*=\s*@{escapedParameterName}\b",
|
||||
match => $"{match.Groups["column"].Value} IN @{parameterName}",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
private static string NormalizePostgresqlCollectionParameter(
|
||||
string sql,
|
||||
string escapedParameterName,
|
||||
string parameterName)
|
||||
{
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
$@"IN\s*\(\s*@{escapedParameterName}\s*\)",
|
||||
$"= ANY(@{parameterName})",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
return Regex.Replace(
|
||||
sql,
|
||||
$@"(?<column>(""[^""]+""|\[[^\]]+\]|`[^`]+`|\w+))\s*=\s*@{escapedParameterName}\b",
|
||||
match => $"{match.Groups["column"].Value} = ANY(@{parameterName})",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -465,6 +465,28 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
|||
}
|
||||
}
|
||||
|
||||
if (listform.WorkflowJson.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
var workflow = JsonSerializer.Deserialize<Workflow>(listform.WorkflowJson);
|
||||
if (workflow != null && workflow.IsFilterUserName)
|
||||
{
|
||||
if (whereParts.Any())
|
||||
{
|
||||
whereParts.Add("AND");
|
||||
}
|
||||
|
||||
// Hem CurrentUserName alanı boş olan kayıtları
|
||||
// hem de ApprovalUserFieldName alanı CurrentUserName'e eşit olan kayıtları getirmek istiyoruz,
|
||||
// Boş olanları getirmemizin sebebi workflow start edebilmektir.
|
||||
// İlk kayıt eklenince onaylayacak kişi atanmaz, böylece o kayıt onaysız olarak kalmaz ve workflow başlatılabilir olur.
|
||||
whereParts.Add(
|
||||
$"(\"{workflow.ApprovalUserFieldName}\" = '{CurrentUser.UserName}' " +
|
||||
$"OR \"{workflow.ApprovalUserFieldName}\" IS NULL " +
|
||||
$"OR \"{workflow.ApprovalUserFieldName}\" = '')"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!whereParts.Any())
|
||||
{
|
||||
whereParts.Add("1 = 1");
|
||||
|
|
|
|||
23
api/src/Sozsoft.Platform.Domain/Queries/Workflow.cs
Normal file
23
api/src/Sozsoft.Platform.Domain/Queries/Workflow.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using Volo.Abp.Domain.Values;
|
||||
|
||||
namespace Sozsoft.Platform.Queries;
|
||||
|
||||
public class Workflow : ValueObject
|
||||
{
|
||||
public string ApprovalUserFieldName { get; set; }
|
||||
public bool IsFilterUserName { get; set; }
|
||||
public string ApprovalDateFieldName { get; set; }
|
||||
public string ApprovalStatusFieldName { get; set; }
|
||||
public string ApprovalDescriptionFieldName { get; set; }
|
||||
|
||||
protected override IEnumerable<object> GetAtomicValues()
|
||||
{
|
||||
yield return ApprovalUserFieldName;
|
||||
yield return IsFilterUserName;
|
||||
yield return ApprovalDateFieldName;
|
||||
yield return ApprovalStatusFieldName;
|
||||
yield return ApprovalDescriptionFieldName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.BlobStoring" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.BlobStoring.FileSystem" Version="10.0.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Sozsoft.Platform;
|
||||
using Sozsoft.Platform.DynamicData;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
|
|
@ -178,7 +182,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
|||
|
||||
public virtual async Task<List<T>> QueryAsync<T>(string sql, string cs, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
var param = new DynamicParameters(parameters);
|
||||
var param = CreateDynamicParameters(parameters);
|
||||
var dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||
|
||||
|
|
@ -188,7 +192,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
|||
|
||||
public virtual async Task<IEnumerable<dynamic>> QueryAsync(string sql, string cs, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
var param = new DynamicParameters(parameters);
|
||||
var param = CreateDynamicParameters(parameters);
|
||||
var dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||
|
||||
|
|
@ -197,7 +201,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
|||
|
||||
public virtual async Task<T> QuerySingleAsync<T>(string sql, string cs, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
var param = new DynamicParameters(parameters);
|
||||
var param = CreateDynamicParameters(parameters);
|
||||
var dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||
|
||||
|
|
@ -206,7 +210,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
|||
|
||||
public virtual async Task<T> ExecuteScalarAsync<T>(string sql, string cs, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
var param = new DynamicParameters(parameters);
|
||||
var param = CreateDynamicParameters(parameters);
|
||||
var dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||
|
||||
|
|
@ -237,13 +241,257 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
|||
|
||||
public virtual async Task<int> ExecuteAsync(string sql, string cs, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
var param = new DynamicParameters(parameters);
|
||||
var param = CreateDynamicParameters(parameters);
|
||||
var dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||
|
||||
return await dbConnection.ExecuteAsync(sql, param, transaction);
|
||||
}
|
||||
|
||||
private static DynamicParameters CreateDynamicParameters(Dictionary<string, object> parameters)
|
||||
{
|
||||
var dynamicParameters = new DynamicParameters();
|
||||
|
||||
if (parameters == null)
|
||||
{
|
||||
return dynamicParameters;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
dynamicParameters.Add(parameter.Key, NormalizeParameterValue(parameter.Value));
|
||||
}
|
||||
|
||||
return dynamicParameters;
|
||||
}
|
||||
|
||||
private static object NormalizeParameterValue(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is JsonElement jsonElement)
|
||||
{
|
||||
return NormalizeJsonElement(jsonElement);
|
||||
}
|
||||
|
||||
if (value is Array array && value is not byte[])
|
||||
{
|
||||
return NormalizeArrayParameter(array);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static object NormalizeArrayParameter(Array values)
|
||||
{
|
||||
var normalizedValues = values
|
||||
.Cast<object>()
|
||||
.Select(NormalizeParameterValue)
|
||||
.Where(value => value != null && value != DBNull.Value)
|
||||
.ToArray();
|
||||
|
||||
if (normalizedValues.Length == 0)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
if (TryBuildGuidArray(normalizedValues, out var guidValues))
|
||||
{
|
||||
return guidValues;
|
||||
}
|
||||
|
||||
if (TryBuildIntArray(normalizedValues, out var intValues))
|
||||
{
|
||||
return intValues;
|
||||
}
|
||||
|
||||
if (TryBuildLongArray(normalizedValues, out var longValues))
|
||||
{
|
||||
return longValues;
|
||||
}
|
||||
|
||||
if (TryBuildDecimalArray(normalizedValues, out var decimalValues))
|
||||
{
|
||||
return decimalValues;
|
||||
}
|
||||
|
||||
if (TryBuildBoolArray(normalizedValues, out var boolValues))
|
||||
{
|
||||
return boolValues;
|
||||
}
|
||||
|
||||
if (TryBuildDateTimeOffsetArray(normalizedValues, out var dateTimeOffsetValues))
|
||||
{
|
||||
return dateTimeOffsetValues;
|
||||
}
|
||||
|
||||
var stringValues = normalizedValues.Select(value => value.ToString()).ToArray();
|
||||
|
||||
if (stringValues.Length == 1 && stringValues[0]?.Contains(PlatformConsts.MultiValueDelimiter) == true)
|
||||
{
|
||||
return stringValues[0].Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
return stringValues;
|
||||
}
|
||||
|
||||
private static object NormalizeJsonElement(JsonElement value)
|
||||
{
|
||||
return value.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => value.GetString(),
|
||||
JsonValueKind.Number when value.TryGetInt32(out var intValue) => intValue,
|
||||
JsonValueKind.Number when value.TryGetInt64(out var longValue) => longValue,
|
||||
JsonValueKind.Number when value.TryGetDecimal(out var decimalValue) => decimalValue,
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
JsonValueKind.Null => null,
|
||||
JsonValueKind.Undefined => null,
|
||||
_ => value.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private static bool TryBuildGuidArray(object[] values, out Guid[] result)
|
||||
{
|
||||
result = new Guid[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is Guid guidValue)
|
||||
{
|
||||
result[i] = guidValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(values[i]?.ToString(), out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryBuildIntArray(object[] values, out int[] result)
|
||||
{
|
||||
result = new int[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is int intValue)
|
||||
{
|
||||
result[i] = intValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(values[i]?.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryBuildLongArray(object[] values, out long[] result)
|
||||
{
|
||||
result = new long[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is long longValue)
|
||||
{
|
||||
result[i] = longValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!long.TryParse(values[i]?.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryBuildDecimalArray(object[] values, out decimal[] result)
|
||||
{
|
||||
result = new decimal[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is decimal decimalValue)
|
||||
{
|
||||
result[i] = decimalValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!decimal.TryParse(values[i]?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryBuildBoolArray(object[] values, out bool[] result)
|
||||
{
|
||||
result = new bool[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is bool boolValue)
|
||||
{
|
||||
result[i] = boolValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bool.TryParse(values[i]?.ToString(), out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryBuildDateTimeOffsetArray(object[] values, out DateTimeOffset[] result)
|
||||
{
|
||||
result = new DateTimeOffset[values.Length];
|
||||
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (values[i] is DateTimeOffset dateTimeOffsetValue)
|
||||
{
|
||||
result[i] = dateTimeOffsetValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (values[i] is DateTime dateTimeValue)
|
||||
{
|
||||
result[i] = dateTimeValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!DateTimeOffset.TryParse(values[i]?.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result[i]))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------ Dispose ------------------
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ public class PlatformDbContext :
|
|||
|
||||
b.Property(x => x.Key).IsRequired().HasMaxLength(128);
|
||||
b.Property(x => x.Path).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.ComponentType).HasMaxLength(32);
|
||||
b.Property(x => x.ComponentPath).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.RouteType).HasMaxLength(64);
|
||||
|
||||
|
|
@ -506,6 +507,7 @@ public class PlatformDbContext :
|
|||
b.Property(x => x.PositionX).IsRequired();
|
||||
b.Property(x => x.PositionY).IsRequired();
|
||||
b.Property(x => x.CompareOutcomesJson).HasColumnType("text");
|
||||
b.HasIndex(x => new { x.ListFormCode, x.Title }).IsUnique();
|
||||
});
|
||||
|
||||
builder.Entity<Note>(b =>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260526185809_Initial")]
|
||||
[Migration("20260606212623_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -3486,6 +3486,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ListFormCode", "Title")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
||||
});
|
||||
|
||||
|
|
@ -4153,6 +4156,10 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("ComponentType")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
|
@ -1604,6 +1604,7 @@ namespace Sozsoft.Platform.Migrations
|
|||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Key = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||
Path = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
ComponentType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
|
||||
ComponentPath = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
RouteType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
Authority = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
|
|
@ -3904,6 +3905,12 @@ namespace Sozsoft.Platform.Migrations
|
|||
table: "Sas_H_ListFormImportLog",
|
||||
column: "ImportId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Sas_H_ListFormWorkflow_ListFormCode_Title",
|
||||
table: "Sas_H_ListFormWorkflow",
|
||||
columns: new[] { "ListFormCode", "Title" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Sas_H_Menu_Code",
|
||||
table: "Sas_H_Menu",
|
||||
|
|
@ -3483,6 +3483,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ListFormCode", "Title")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
||||
});
|
||||
|
||||
|
|
@ -4150,6 +4153,10 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("ComponentType")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@
|
|||
"Summary": "blog.posts.ai.excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/8386434/pexels-photo-8386434.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.technology",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"Title": "blog.posts.web.title",
|
||||
|
|
@ -213,7 +213,7 @@
|
|||
"Summary": "blog.posts.web.excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/11035471/pexels-photo-11035471.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.webdev",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"Title": "blog.posts.security.title",
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
"Summary": "blog.posts.security.excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/5380642/pexels-photo-5380642.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.security",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"Title": "blog.posts.mobile.title",
|
||||
|
|
@ -235,7 +235,7 @@
|
|||
"ReadTime": "4 dk",
|
||||
"CoverImage": "https://images.pexels.com/photos/13017583/pexels-photo-13017583.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.mobile",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"Title": "blog.posts.database.title",
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
"ReadTime": "8 dk",
|
||||
"CoverImage": "https://images.pexels.com/photos/325229/pexels-photo-325229.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.database",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"Title": "blog.posts.digital.title",
|
||||
|
|
@ -257,7 +257,7 @@
|
|||
"ReadTime": "6 dk",
|
||||
"CoverImage": "https://images.pexels.com/photos/7681091/pexels-photo-7681091.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CategoryName": "blog.categories.digital",
|
||||
"Author": "SYSTEM@SOZSOFT.COM"
|
||||
"Author": "system@sozsoft.com"
|
||||
}
|
||||
],
|
||||
"GlobalSearch": [
|
||||
|
|
@ -324,16 +324,16 @@
|
|||
"Url": "/dil/",
|
||||
"Method": "GET",
|
||||
"DataSourceCode": "Default",
|
||||
"Sql": "SELECT * FROM Plat_H_Language WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName",
|
||||
"Sql": "SELECT * FROM Sas_H_Language WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName",
|
||||
"ParametersJson": [
|
||||
{
|
||||
"Type": "P",
|
||||
"Type": "Path",
|
||||
"Name": "CultureName",
|
||||
"DefaultValue": "ar",
|
||||
"Path": "/dil/:CultureName/"
|
||||
},
|
||||
{
|
||||
"Type": "S",
|
||||
"Type": "Static",
|
||||
"Name": "IsEnabled",
|
||||
"DefaultValue": "true"
|
||||
}
|
||||
|
|
@ -341,7 +341,7 @@
|
|||
"PermissionsJson": [
|
||||
{
|
||||
"ResourceType": "User",
|
||||
"ResourceId": "SYSTEM@SOZSOFT.COM"
|
||||
"ResourceId": "system@sozsoft.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -429,6 +429,153 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"Homes": [
|
||||
{
|
||||
"heroBackgroundImageKey": "Public.home.hero.backgroundImage",
|
||||
"heroPrimaryCtaKey": "Public.hero.cta.consultation",
|
||||
"heroSecondaryCtaKey": "Public.hero.cta.discover",
|
||||
"featuresTitleKey": "Public.features.title",
|
||||
"featuresSubtitleKey": "Public.features.subtitle",
|
||||
"solutionsTitleKey": "Public.solutions.title",
|
||||
"solutionsSubtitleKey": "Public.solutions.subtitle",
|
||||
"ctaTitleKey": "Public.common.getStarted",
|
||||
"ctaSubtitleKey": "Public.common.contact",
|
||||
"ctaButtonLabelKey": "Public.common.learnMore",
|
||||
"slides": [
|
||||
{
|
||||
"titleKey": "Public.hero.slide1.title",
|
||||
"subtitleKey": "Public.hero.slide1.subtitle",
|
||||
"services": [
|
||||
{
|
||||
"icon": "FaCalendarAlt",
|
||||
"titleKey": "Public.hero.slide1.service1.title",
|
||||
"descriptionKey": "Public.hero.slide1.service1.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaUsers",
|
||||
"titleKey": "Public.hero.slide1.service2.title",
|
||||
"descriptionKey": "Public.hero.slide1.service2.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaShieldAlt",
|
||||
"titleKey": "Public.hero.slide1.service3.title",
|
||||
"descriptionKey": "Public.hero.slide1.service3.desc"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"titleKey": "Public.hero.slide2.title",
|
||||
"subtitleKey": "Public.hero.slide2.subtitle",
|
||||
"services": [
|
||||
{
|
||||
"icon": "FaChartBar",
|
||||
"titleKey": "Public.hero.slide2.service1.title",
|
||||
"descriptionKey": "Public.hero.slide2.service1.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaCreditCard",
|
||||
"titleKey": "Public.hero.slide2.service2.title",
|
||||
"descriptionKey": "Public.hero.slide2.service2.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaDatabase",
|
||||
"titleKey": "Public.hero.slide2.service3.title",
|
||||
"descriptionKey": "Public.hero.slide2.service3.desc"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"titleKey": "Public.hero.slide3.title",
|
||||
"subtitleKey": "Public.hero.slide3.subtitle",
|
||||
"services": [
|
||||
{
|
||||
"icon": "FaDesktop",
|
||||
"titleKey": "Public.hero.slide3.service1.title",
|
||||
"descriptionKey": "Public.hero.slide3.service1.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaServer",
|
||||
"titleKey": "Public.hero.slide3.service2.title",
|
||||
"descriptionKey": "Public.hero.slide3.service2.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaMobileAlt",
|
||||
"titleKey": "Public.hero.slide3.service3.title",
|
||||
"descriptionKey": "Public.hero.slide3.service3.desc"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
{
|
||||
"icon": "FaUsers",
|
||||
"titleKey": "Public.features.reliable",
|
||||
"descriptionKey": "Public.features.reliable.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaCalendarAlt",
|
||||
"titleKey": "App.Videoroom.Planning",
|
||||
"descriptionKey": "Public.features.rapid.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaBookOpen",
|
||||
"titleKey": "Public.features.expert",
|
||||
"descriptionKey": "Public.features.expert.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaCreditCard",
|
||||
"titleKey": "Public.features.muhasebe",
|
||||
"descriptionKey": "Public.features.muhasebe.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaRegComment",
|
||||
"titleKey": "Public.features.iletisim",
|
||||
"descriptionKey": "Public.features.iletisim.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaPhone",
|
||||
"titleKey": "Public.features.mobil",
|
||||
"descriptionKey": "Public.features.mobil.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaChartBar",
|
||||
"titleKey": "Public.features.scalable",
|
||||
"descriptionKey": "Public.features.scalable.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaShieldAlt",
|
||||
"titleKey": "Public.features.guvenlik",
|
||||
"descriptionKey": "Public.features.guvenlik.desc"
|
||||
}
|
||||
],
|
||||
"solutions": [
|
||||
{
|
||||
"icon": "FaDesktop",
|
||||
"colorClass": "bg-blue-600",
|
||||
"titleKey": "Public.services.web.title",
|
||||
"descriptionKey": "Public.solutions.web.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaMobileAlt",
|
||||
"colorClass": "bg-purple-600",
|
||||
"titleKey": "Public.services.mobile.title",
|
||||
"descriptionKey": "Public.solutions.mobile.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaServer",
|
||||
"colorClass": "bg-green-600",
|
||||
"titleKey": "Public.solutions.custom.title",
|
||||
"descriptionKey": "Public.solutions.custom.desc"
|
||||
},
|
||||
{
|
||||
"icon": "FaDatabase",
|
||||
"colorClass": "bg-red-600",
|
||||
"titleKey": "Public.solutions.database.title",
|
||||
"descriptionKey": "Public.solutions.database.desc"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Services": [
|
||||
{
|
||||
"icon": "FaCode",
|
||||
|
|
@ -1309,6 +1456,36 @@
|
|||
"DepartmentName": "Muhasebe",
|
||||
"Name": "Muhasebe Şefi",
|
||||
"ParentName": "Muhasebe Müdürü"
|
||||
},
|
||||
{
|
||||
"Id": "b7c8d9e0-f1a2-4b3c-8d9e-0f1a2b3c4d2e",
|
||||
"DepartmentName": "Bilgi İşlem",
|
||||
"Name": "Bilgi İşlem Müdürü",
|
||||
"ParentName": "Genel Müdür"
|
||||
},
|
||||
{
|
||||
"Id": "b7c8d9e0-f1a2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||
"DepartmentName": "Finans",
|
||||
"Name": "Finans Müdürü",
|
||||
"ParentName": "Genel Müdür"
|
||||
},
|
||||
{
|
||||
"Id": "b7c8d9e0-f1b2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||
"DepartmentName": "Satış",
|
||||
"Name": "İhracat Müdürü",
|
||||
"ParentName": "Genel Müdür"
|
||||
},
|
||||
{
|
||||
"Id": "b2c8d9e0-f1b2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||
"DepartmentName": "Satış",
|
||||
"Name": "İç Piyasa Müdürü",
|
||||
"ParentName": "Genel Müdür"
|
||||
},
|
||||
{
|
||||
"Id": "b2c8d9e0-f1b2-2b3c-1d9e-0f1a2b3c4d2e",
|
||||
"DepartmentName": "Üretim",
|
||||
"Name": "Üretim Müdürü",
|
||||
"ParentName": "Genel Müdür"
|
||||
}
|
||||
],
|
||||
"Announcements": [
|
||||
|
|
@ -1317,7 +1494,7 @@
|
|||
"content": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.",
|
||||
"excerpt": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!",
|
||||
"category": "general",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "12-10-2024",
|
||||
"isPinned": true,
|
||||
"viewCount": 0,
|
||||
|
|
@ -1328,7 +1505,7 @@
|
|||
"content": "Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.",
|
||||
"excerpt": "Yıl sonu performans değerlendirmeleri başlıyor.",
|
||||
"category": "event",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "08-10-2024",
|
||||
"expiryDate": "05-11-2024",
|
||||
"isPinned": true,
|
||||
|
|
@ -1339,7 +1516,7 @@
|
|||
"content": "Bu Cumartesi saat 02: 00 - 06: 00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.",
|
||||
"excerpt": "Cumartesi gecesi planlı bakım çalışması",
|
||||
"category": "urgent",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "08-10-2024",
|
||||
"isPinned": false,
|
||||
"viewCount": 0
|
||||
|
|
@ -1349,7 +1526,7 @@
|
|||
"content": "Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.",
|
||||
"excerpt": "React İleri Seviye eğitimi kayıtları başladı",
|
||||
"category": "event",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "09-10-2024",
|
||||
"isPinned": false,
|
||||
"viewCount": 0
|
||||
|
|
@ -1359,7 +1536,7 @@
|
|||
"content": "Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.",
|
||||
"excerpt": "Güvenlik politikası güncellendi - Onay gerekli",
|
||||
"category": "urgent",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "04-10-2024",
|
||||
"isPinned": true,
|
||||
"viewCount": 0
|
||||
|
|
@ -1533,42 +1710,42 @@
|
|||
"SocialPosts": [
|
||||
{
|
||||
"content": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "Bugün müşteri ile harika bir toplantı yaptık! Yeni projenin detaylarını konuştuk. 🎯",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 0,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
|
|
@ -1654,53 +1831,53 @@
|
|||
"SocialComments": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Harika görünüyor! Başarılar 👏"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "TypeScript gerçekten fark yaratıyor!"
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Mesajlaşma özelliğine kesinlikle ihtiyacımız var!"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Tasarımlar çok şık! Renk paleti özellikle güzel 😍"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Dark mode opsiyonu da olacak mı?"
|
||||
},
|
||||
{
|
||||
"postContent": "CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Harika iş! Detayları paylaşabilir misin?"
|
||||
},
|
||||
{
|
||||
"postContent": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Ne zaman başlıyor?"
|
||||
},
|
||||
{
|
||||
"postContent": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "SYSTEM@SOZSOFT.COM",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Gelecek hafta başlıyoruz! Kayıt linki mail ile paylaşılacak."
|
||||
}
|
||||
],
|
||||
"SocialLikes": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "SYSTEM@SOZSOFT.COM"
|
||||
"userName": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "SYSTEM@SOZSOFT.COM"
|
||||
"userName": "system@sozsoft.com"
|
||||
}
|
||||
],
|
||||
"EventTypes": [
|
||||
|
|
@ -1881,7 +2058,7 @@
|
|||
"Description": "Tüm departmanların katılımıyla düzenlenen geleneksel yaz futbol turnuvası.",
|
||||
"Place": "Şirket Kampüsü Spor Alanı",
|
||||
"Status": "published",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"ParticipantsCount": 64,
|
||||
"IsPublished": true,
|
||||
"Likes": 0,
|
||||
|
|
@ -1896,7 +2073,7 @@
|
|||
"Description": "Çalışanlarımıza özel, rehber eşliğinde 2 günlük kültürel gezi.",
|
||||
"Place": "Kapadokya, Nevşehir",
|
||||
"Status": "published",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"ParticipantsCount": 25,
|
||||
"IsPublished": true,
|
||||
"Likes": 0,
|
||||
|
|
@ -1911,7 +2088,7 @@
|
|||
"Description": "Caz müziğinin en güzel örneklerinin canlı performanslarla sunulacağı özel akşam.",
|
||||
"Place": "Şirket Konferans Salonu",
|
||||
"Status": "published",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"ParticipantsCount": 40,
|
||||
"IsPublished": true,
|
||||
"Likes": 0,
|
||||
|
|
@ -1922,55 +2099,55 @@
|
|||
"EventComments": [
|
||||
{
|
||||
"EventName": "Yaz Futbol Turnuvası 2025",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Muhteşem bir gündü! Yılın en güzel etkinliği 🎉",
|
||||
"Likes": 12
|
||||
},
|
||||
{
|
||||
"EventName": "Yaz Futbol Turnuvası 2025",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Voleybol turnuvası harikaydı, gelecek yıl yine yapalım!",
|
||||
"Likes": 8
|
||||
},
|
||||
{
|
||||
"EventName": "Kültür Gezisi: Kapadokya",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Ekibimiz 2. oldu! Çok gurur duydum herkesle 💪",
|
||||
"Likes": 15
|
||||
},
|
||||
{
|
||||
"EventName": "Kültür Gezisi: Kapadokya",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Gece boyunca kod yazmak ve pizza yemek priceless! 🍕",
|
||||
"Likes": 10
|
||||
},
|
||||
{
|
||||
"EventName": "Müzik Dinletisi: Jazz Akşamı",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "İT departmanı şampiyon oldu! Gelecek sene kupayı koruyacağız 🏆",
|
||||
"Likes": 18
|
||||
},
|
||||
{
|
||||
"EventName": "Müzik Dinletisi: Jazz Akşamı",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Yılın en şık gecesi! Organizasyon mükemmeldi 👏",
|
||||
"Likes": 25
|
||||
},
|
||||
{
|
||||
"EventName": "Müzik Dinletisi: Jazz Akşamı",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Tombala hediyelerim harika, çok teşekkürler! 🎁",
|
||||
"Likes": 14
|
||||
},
|
||||
{
|
||||
"EventName": "Müzik Dinletisi: Jazz Akşamı",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "Müzik grubunuz süperdi, dans pistinden ayrılamadık! 🎵",
|
||||
"Likes": 19
|
||||
},
|
||||
{
|
||||
"EventName": "Müzik Dinletisi: Jazz Akşamı",
|
||||
"UserName": "SYSTEM@SOZSOFT.COM",
|
||||
"UserName": "system@sozsoft.com",
|
||||
"Content": "İlk defa ebru yaptım, çok huzurlu bir deneyimdi 🎨",
|
||||
"Likes": 11
|
||||
}
|
||||
|
|
@ -2077,4 +2254,4 @@
|
|||
"ResetPeriod": "Daily"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ using Volo.Abp.Identity;
|
|||
using Volo.Abp.Timing;
|
||||
using System.Collections.Generic;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using System.Linq;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Sozsoft.Platform.Data.Seeds;
|
||||
|
||||
|
|
@ -40,6 +40,7 @@ public class TenantSeederDto
|
|||
public List<JobPositionSeedDto> JobPositions { get; set; }
|
||||
|
||||
//Public
|
||||
public List<HomeSeedDto> Homes { get; set; }
|
||||
public List<AboutSeedDto> Abouts { get; set; }
|
||||
public List<ServiceSeedDto> Services { get; set; }
|
||||
public List<PaymentMethodSeedDto> PaymentMethods { get; set; }
|
||||
|
|
@ -392,6 +393,56 @@ public class AboutSeedDto
|
|||
public List<SectionDto> Sections { get; set; }
|
||||
}
|
||||
|
||||
public class HomeSeedDto
|
||||
{
|
||||
public string HeroBackgroundImageKey { get; set; }
|
||||
public string HeroPrimaryCtaKey { get; set; }
|
||||
public string HeroSecondaryCtaKey { get; set; }
|
||||
public string FeaturesTitleKey { get; set; }
|
||||
public string FeaturesSubtitleKey { get; set; }
|
||||
public string SolutionsTitleKey { get; set; }
|
||||
public string SolutionsSubtitleKey { get; set; }
|
||||
public string CtaTitleKey { get; set; }
|
||||
public string CtaSubtitleKey { get; set; }
|
||||
public string CtaButtonLabelKey { get; set; }
|
||||
public List<HomeSlideSeedDto> Slides { get; set; }
|
||||
public List<HomeFeatureSeedDto> Features { get; set; }
|
||||
public List<HomeSolutionSeedDto> Solutions { get; set; }
|
||||
}
|
||||
|
||||
public class HomeSlideSeedDto
|
||||
{
|
||||
public string TitleKey { get; set; }
|
||||
public string SubtitleKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
public List<HomeSlideServiceSeedDto> Services { get; set; }
|
||||
}
|
||||
|
||||
public class HomeSlideServiceSeedDto
|
||||
{
|
||||
public string Icon { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class HomeFeatureSeedDto
|
||||
{
|
||||
public string Icon { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class HomeSolutionSeedDto
|
||||
{
|
||||
public string Icon { get; set; }
|
||||
public string ColorClass { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class WorkHourSeedDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
|
@ -434,12 +485,14 @@ public class ProductSeedDto
|
|||
public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
||||
{
|
||||
private readonly IClock _clock;
|
||||
private readonly ILookupNormalizer _lookupNormalizer;
|
||||
private readonly IRepository<Branch, Guid> _branchRepository;
|
||||
private readonly IRepository<GlobalSearch, int> _globalSearch;
|
||||
private readonly IRepository<CustomEndpoint, Guid> _customEndpointRepository;
|
||||
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
||||
private readonly IRepository<ReportCategory, Guid> _reportCategoriesRepository;
|
||||
private readonly IRepository<ReportTemplate, Guid> _reportTemplatesRepository;
|
||||
private readonly IRepository<Home, Guid> _homeRepository;
|
||||
private readonly IRepository<About, Guid> _aboutRepository;
|
||||
private readonly IRepository<PaymentMethod, Guid> _paymentMethodRepository;
|
||||
private readonly IRepository<InstallmentOption, Guid> _installmentOptionRepository;
|
||||
|
|
@ -482,6 +535,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
|
||||
public TenantDataSeeder(
|
||||
IClock clock,
|
||||
ILookupNormalizer lookupNormalizer,
|
||||
IIdentityUserRepository repositoryUser,
|
||||
IRepository<Branch, Guid> branchRepository,
|
||||
IRepository<GlobalSearch, int> globalSearch,
|
||||
|
|
@ -495,6 +549,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
IRepository<CustomComponent, Guid> customComponentRepository,
|
||||
IRepository<ReportCategory, Guid> reportCategoriesRepository,
|
||||
IRepository<ReportTemplate, Guid> reportTemplatesRepository,
|
||||
IRepository<Home, Guid> homeRepository,
|
||||
IRepository<About, Guid> aboutRepository,
|
||||
IRepository<Service, Guid> servicesRepository,
|
||||
IRepository<Product, Guid> productRepository,
|
||||
|
|
@ -530,6 +585,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
)
|
||||
{
|
||||
_clock = clock;
|
||||
_lookupNormalizer = lookupNormalizer;
|
||||
_repositoryUser = repositoryUser;
|
||||
_branchRepository = branchRepository;
|
||||
_globalSearch = globalSearch;
|
||||
|
|
@ -548,6 +604,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
_customComponentRepository = customComponentRepository;
|
||||
_reportCategoriesRepository = reportCategoriesRepository;
|
||||
_reportTemplatesRepository = reportTemplatesRepository;
|
||||
_homeRepository = homeRepository;
|
||||
_servicesRepository = servicesRepository;
|
||||
_aboutRepository = aboutRepository;
|
||||
_contactRepository = contactRepository;
|
||||
|
|
@ -848,6 +905,30 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var item in items.Homes)
|
||||
{
|
||||
var exists = await _homeRepository.FirstOrDefaultAsync();
|
||||
if (exists == null)
|
||||
{
|
||||
await _homeRepository.InsertAsync(new Home
|
||||
{
|
||||
HeroBackgroundImageKey = item.HeroBackgroundImageKey,
|
||||
HeroPrimaryCtaKey = item.HeroPrimaryCtaKey,
|
||||
HeroSecondaryCtaKey = item.HeroSecondaryCtaKey,
|
||||
FeaturesTitleKey = item.FeaturesTitleKey,
|
||||
FeaturesSubtitleKey = item.FeaturesSubtitleKey,
|
||||
SolutionsTitleKey = item.SolutionsTitleKey,
|
||||
SolutionsSubtitleKey = item.SolutionsSubtitleKey,
|
||||
CtaTitleKey = item.CtaTitleKey,
|
||||
CtaSubtitleKey = item.CtaSubtitleKey,
|
||||
CtaButtonLabelKey = item.CtaButtonLabelKey,
|
||||
SlidesJson = JsonSerializer.Serialize(item.Slides),
|
||||
FeaturesJson = JsonSerializer.Serialize(item.Features),
|
||||
SolutionsJson = JsonSerializer.Serialize(item.Solutions)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in items.Contacts)
|
||||
{
|
||||
var exists = await _contactRepository.FirstOrDefaultAsync();
|
||||
|
|
@ -993,7 +1074,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
|
||||
await _announcementRepository.InsertAsync(new Announcement
|
||||
{
|
||||
Title = item.Title,
|
||||
|
|
@ -1067,7 +1148,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
|
||||
|
||||
await _socialPostRepository.InsertAsync(new SocialPost(Guid.NewGuid())
|
||||
{
|
||||
|
|
@ -1157,7 +1238,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
|
||||
await _socialCommentRepository.InsertAsync(new SocialComment(Guid.NewGuid())
|
||||
{
|
||||
UserId = user != null ? user.Id : null,
|
||||
|
|
@ -1177,7 +1258,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
|
||||
await _socialLikeRepository.InsertAsync(new SocialLike(Guid.NewGuid())
|
||||
{
|
||||
SocialPostId = post != null ? post.Id : Guid.Empty,
|
||||
|
|
@ -1218,7 +1299,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
{
|
||||
var category = await _eventCategoryRepository.FirstOrDefaultAsync(x => x.Name == item.CategoryName);
|
||||
var type = await _eventTypeRepository.FirstOrDefaultAsync(x => x.Name == item.TypeName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
|
||||
|
||||
if (category != null && type != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,10 +133,10 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
|
|||
Surname = PlatformConsts.AbpIdentity.User.AdminSurNameDefaultValue,
|
||||
};
|
||||
|
||||
adminUser.SetEmailConfirmed(true);
|
||||
adminUser.SetIsVerified(true);
|
||||
adminUser.SetEmailConfirmed(true);
|
||||
adminUser.SetRocketUsername(PlatformConsts.AbpIdentity.User.AdminRocketUsernameDefaultValue);
|
||||
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, true);
|
||||
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, adminUser.PhoneNumberConfirmed);
|
||||
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
|
||||
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
|
||||
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" Version="10.0.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
|
||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -125,10 +125,9 @@ public class SetupController : ControllerBase
|
|||
{
|
||||
try
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
while (await reader.ReadLineAsync(ct) is { } line)
|
||||
{
|
||||
var line = await reader.ReadLineAsync(ct);
|
||||
if (line != null) await Send(level, line);
|
||||
await Send(level, line);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
|
|
|
|||
|
|
@ -264,10 +264,9 @@ internal static class SetupAppRunner
|
|||
{
|
||||
try
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
while (await reader.ReadLineAsync(ct) is { } line)
|
||||
{
|
||||
var line = await reader.ReadLineAsync(ct);
|
||||
if (line != null) await Send(level, line);
|
||||
await Send(level, line);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
|
|
|
|||
|
|
@ -187,9 +187,9 @@ public class PlatformHttpApiHostModule : AbpModule
|
|||
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());
|
||||
|
||||
options.Applications[PlatformConsts.React].RootUrl = configuration["App:ClientUrl"];
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.EmailConfirmation] = "account/confirm";
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.PasswordReset] = "account/reset-password";
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.UserDetail] = "account/{0}";
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.EmailConfirmation] = "confirm";
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.PasswordReset] = "reset-password";
|
||||
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.UserDetail] = "admin/users/detail";
|
||||
|
||||
//options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
|
||||
//options.Applications["MVC"].Urls[PlatformConsts.Urls.EmailConfirmation] = "Account/Confirm";
|
||||
|
|
@ -525,10 +525,26 @@ public class PlatformHttpApiHostModule : AbpModule
|
|||
app.UseConfiguredEndpoints();
|
||||
}
|
||||
|
||||
public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
|
||||
public override Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
|
||||
{
|
||||
using var scope = context.ServiceProvider.CreateScope();
|
||||
var initializer = scope.ServiceProvider.GetRequiredService<BackgroundWorkerInitializer>();
|
||||
await initializer.RunAsync();
|
||||
var serviceProvider = context.ServiceProvider;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<PlatformHttpApiHostModule>>();
|
||||
|
||||
try
|
||||
{
|
||||
var initializer = scope.ServiceProvider.GetRequiredService<BackgroundWorkerInitializer>();
|
||||
await initializer.RunAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Background worker initialization failed.");
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>Sozsoft.Platform</RootNamespace>
|
||||
<PreserveCompilationReferences>true</PreserveCompilationReferences>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -117,6 +117,38 @@ server {
|
|||
|
||||
}
|
||||
|
||||
# dashboard.sozsoft.com
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name dashboard.sozsoft.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/dashboard.sozsoft.com/fullchain.pem;
|
||||
ssl_trusted_certificate /etc/ssl/sozsoft.com/chain1.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/dashboard.sozsoft.com/privkey.pem;
|
||||
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
#sudo htpasswd -c /etc/nginx/.htpasswd sedat.ozturk
|
||||
#yukarıdaki komut ile kullanıcı adı ve şifre oluşturabilirsiniz
|
||||
|
||||
proxy_headers_hash_max_size 2048;
|
||||
proxy_headers_hash_bucket_size 128;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:19999;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
include /etc/nginx/proxy_params;
|
||||
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
}
|
||||
}
|
||||
|
||||
# sozsoft.com
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ volumes:
|
|||
rocket_mongodb_data:
|
||||
driver: local
|
||||
n8n_data:
|
||||
netdataconfig:
|
||||
netdatalib:
|
||||
netdatacache:
|
||||
|
||||
services:
|
||||
forgejo:
|
||||
|
|
@ -108,3 +111,30 @@ services:
|
|||
- /etc/ssl/sozsoft.com:/etc/ssl/sozsoft.com:ro # Sertifikaları mount ettik
|
||||
- ./logs/coturn:/var/log # Logları dışarı al (opsiyonel)
|
||||
command: ["turnserver", "-c", "/etc/coturn/turnserver.conf"]
|
||||
|
||||
dashboard:
|
||||
image: netdata/netdata:stable
|
||||
container_name: dashboard
|
||||
hostname: kursserver
|
||||
restart: unless-stopped
|
||||
pid: host
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
- SYS_ADMIN
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
volumes:
|
||||
- netdataconfig:/etc/netdata
|
||||
- netdatalib:/var/lib/netdata
|
||||
- netdatacache:/var/cache/netdata
|
||||
- /:/host/root:ro,rslave
|
||||
- /etc/passwd:/host/etc/passwd:ro
|
||||
- /etc/group:/host/etc/group:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /etc/os-release:/host/etc/os-release:ro
|
||||
- /var/log:/host/var/log:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- /run/dbus:/run/dbus:ro
|
||||
|
|
@ -20,6 +20,7 @@ SUBDOMAINS=(
|
|||
"sozsoft.com"
|
||||
"www.sozsoft.com"
|
||||
"demo.sozsoft.com"
|
||||
"dashboard.sozsoft.com"
|
||||
)
|
||||
|
||||
echo "Subdomain'ler için SSL sertifikaları alınıyor..."
|
||||
|
|
|
|||
|
|
@ -1,6 +1,41 @@
|
|||
{
|
||||
"commit": "0b5eb3d",
|
||||
"commit": "dc293fc",
|
||||
"releases": [
|
||||
{
|
||||
"version": "1.1.04",
|
||||
"buildDate": "2026-06-04",
|
||||
"commit": "20e7fae481ce69e9a678508ce03b5ed7831aea9f",
|
||||
"changeLog": [
|
||||
"- Settingde yapılan ayarlar Auth komponentlerine uygulandı.",
|
||||
"- Public home ve diğer sayfaların tasarım değişikliği yapıldı.",
|
||||
"- Route Type Dinamik ve Normal olarak ayrıldı.",
|
||||
"- Form Devexpress DefaultValue özelliği eklendi.",
|
||||
"- Devexpress DarkModa uygun şekilde güncellendi.",
|
||||
"- Grid, Tree ve FormDevexpress setReadonly özelliği eklendi."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.03",
|
||||
"buildDate": "2026-05-30",
|
||||
"commit": "96f7091d46c248ba3c42849fe5d870db0ab96982",
|
||||
"changeLog": [
|
||||
"- User Detail komponentinin içerisinde Avatar ekleme",
|
||||
"- EditForm un içerisinde EditorOptions dinamik oluşturulması",
|
||||
"- Editform un içerisinde EditorScript dinamik oluşturulması"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.02",
|
||||
"buildDate": "2026-05-27",
|
||||
"commit": "84b9f6510787bd82f3797728fd675f755b4caa2d",
|
||||
"changeLog": [
|
||||
"- .NET 10 yükseltildi.",
|
||||
"- Abp Framework 10 yükseltildi.",
|
||||
"- Sql Query Manager problemleri giderildi.",
|
||||
"- AuditLog problemleri giderildi.",
|
||||
"- Tenan yapısını uygunluğu için düzenlemeler yapıldı."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.01",
|
||||
"buildDate": "2026-05-24",
|
||||
|
|
@ -115,4 +150,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import Card from '@/components/ui/Card'
|
|||
import Logo from '@/components/template/Logo'
|
||||
import type { ReactNode, ReactElement } from 'react'
|
||||
import type { CommonProps } from '@/proxy/common'
|
||||
import { FaArrowLeft, FaCheck } from 'react-icons/fa';
|
||||
import { FaArrowLeft, FaCheck } from 'react-icons/fa'
|
||||
import { Avatar, Select } from '@/components/ui'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import appConfig from '@/proxy/configs/app.config'
|
||||
|
|
@ -90,7 +90,7 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
|
|||
return (
|
||||
<div className="h-full">
|
||||
<Container className="flex flex-col flex-auto items-center justify-center min-w-0 h-full">
|
||||
<Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-5">
|
||||
<Card className="w-full min-w-[320px] max-w-[360px] md:min-w-[450px]" bodyClass="md:p-5">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
{!hasSubdomain() && (
|
||||
<a
|
||||
|
|
|
|||
|
|
@ -121,14 +121,14 @@ const PublicLayout = () => {
|
|||
onClick={() => setThemeMode(THEME_ENUM.MODE_LIGHT)}
|
||||
aria-pressed={!isDarkMode}
|
||||
title="Light mode"
|
||||
className={`inline-flex h-8 items-center gap-1.5 px-2.5 text-xs font-semibold transition-colors rounded-l-md ${
|
||||
className={`inline-flex h-8 w-8 xl:w-auto items-center justify-center gap-1.5 px-0 xl:px-2.5 text-xs font-semibold transition-colors rounded-l-md ${
|
||||
!isDarkMode
|
||||
? 'bg-white text-gray-950'
|
||||
: 'text-gray-300 hover:bg-white/10 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<LuSun size={15} strokeWidth={1.8} />
|
||||
{translate('::App.Light')}
|
||||
<span className="hidden xl:inline">{translate('::App.Light')}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -136,12 +136,12 @@ const PublicLayout = () => {
|
|||
onClick={() => setThemeMode(THEME_ENUM.MODE_DARK)}
|
||||
aria-pressed={isDarkMode}
|
||||
title="Dark mode"
|
||||
className={`inline-flex h-8 items-center gap-1.5 px-2.5 text-xs font-semibold transition-colors rounded-r-md ${
|
||||
className={`inline-flex h-8 w-8 xl:w-auto items-center justify-center gap-1.5 px-0 xl:px-2.5 text-xs font-semibold transition-colors rounded-r-md ${
|
||||
isDarkMode ? 'bg-white text-gray-950' : 'text-gray-300 hover:bg-white/10 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<LuMoon size={15} strokeWidth={1.8} />
|
||||
{translate('::App.Dark')}
|
||||
<span className="hidden xl:inline">{translate('::App.Dark')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -163,11 +163,15 @@ const PublicLayout = () => {
|
|||
: 'bg-gray-950/80 backdrop-blur-sm py-4 border-b border-white/5'
|
||||
}`}
|
||||
>
|
||||
<div className="container mx-auto px-6 flex items-center justify-between">
|
||||
<Logo mode="dark" />
|
||||
<div className="container mx-auto px-6 relative grid grid-cols-[auto_1fr_auto] items-center lg:grid-cols-[1fr_auto] xl:grid-cols-[auto_1fr_auto]">
|
||||
<Logo
|
||||
mode="dark"
|
||||
className="relative z-10 max-w-[190px] overflow-hidden lg:hidden xl:block xl:max-w-none"
|
||||
imgClass="h-10 w-auto object-contain xl:h-auto"
|
||||
/>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center gap-1">
|
||||
{/* Desktop / tablet navigation */}
|
||||
<nav className="hidden lg:flex lg:col-start-1 xl:col-start-2 items-center justify-self-start xl:justify-self-center gap-1">
|
||||
{navLinks
|
||||
.filter((l) => !isLoginLink(l.resourceKey))
|
||||
.map((link) => {
|
||||
|
|
@ -216,7 +220,7 @@ const PublicLayout = () => {
|
|||
</nav>
|
||||
|
||||
{/* Right side: Language + Login */}
|
||||
<div className="hidden md:flex items-center gap-3">
|
||||
<div className="relative z-10 hidden lg:flex lg:col-start-2 xl:col-start-3 lg:justify-self-end items-center gap-2 xl:gap-3">
|
||||
<LanguageSelector />
|
||||
{themeToggle}
|
||||
<div className="w-px h-5 bg-white/20" />
|
||||
|
|
@ -237,9 +241,10 @@ const PublicLayout = () => {
|
|||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="md:hidden flex items-center justify-center w-9 h-9 text-white rounded-md hover:bg-white/10 transition-colors"
|
||||
className="lg:hidden col-start-3 justify-self-end flex items-center justify-center w-9 h-9 text-white rounded-md hover:bg-white/10 transition-colors"
|
||||
onClick={toggleMenu}
|
||||
aria-label="Toggle menu"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
{isOpen ? <LuX size={20} strokeWidth={2} /> : <LuMenu size={20} strokeWidth={2} />}
|
||||
</button>
|
||||
|
|
@ -247,68 +252,88 @@ const PublicLayout = () => {
|
|||
|
||||
{/* Mobile Navigation */}
|
||||
<div
|
||||
className={`md:hidden overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
className={`lg:hidden overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
isOpen ? 'max-h-screen opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}
|
||||
>
|
||||
<div className="bg-gray-950/98 backdrop-blur-md border-t border-white/10">
|
||||
<div className="border-t border-white/10 bg-gray-950/98 backdrop-blur-md">
|
||||
<div className="container mx-auto px-6 py-3">
|
||||
<nav className="flex flex-col gap-1">
|
||||
{navLinks.map((link) => {
|
||||
const active = isActiveLink(link.path)
|
||||
const isLogin = isLoginLink(link.resourceKey)
|
||||
if (isLogin) {
|
||||
<div className="lg:hidden flex flex-col gap-1">
|
||||
{navLinks.map((link) => {
|
||||
const active = isActiveLink(link.path)
|
||||
const isLogin = isLoginLink(link.resourceKey)
|
||||
if (isLogin) {
|
||||
return link.path ? (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
onClick={toggleMenu}
|
||||
className="mt-2 flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg transition-colors"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
) : null
|
||||
}
|
||||
return link.path ? (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
onClick={toggleMenu}
|
||||
className="mt-2 flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg transition-colors"
|
||||
className={`flex items-center gap-2.5 px-3 py-2.5 text-sm font-medium rounded-md transition-colors ${
|
||||
active
|
||||
? 'bg-white/10 text-white'
|
||||
: 'text-gray-300 hover:bg-white/5 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{link.icon && (
|
||||
<link.icon
|
||||
size={16}
|
||||
strokeWidth={1.75}
|
||||
className={active ? 'text-blue-400' : 'text-gray-400'}
|
||||
/>
|
||||
)}
|
||||
{link.name}
|
||||
</Link>
|
||||
) : null
|
||||
}
|
||||
return link.path ? (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
onClick={toggleMenu}
|
||||
className={`flex items-center gap-2.5 px-3 py-2.5 text-sm font-medium rounded-md transition-colors ${
|
||||
active
|
||||
? 'bg-white/10 text-white'
|
||||
: 'text-gray-300 hover:bg-white/5 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{link.icon && (
|
||||
<link.icon
|
||||
size={16}
|
||||
strokeWidth={1.75}
|
||||
className={active ? 'text-blue-400' : 'text-gray-400'}
|
||||
/>
|
||||
)}
|
||||
{link.name}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
key={link.name}
|
||||
onClick={() => {
|
||||
link.action?.()
|
||||
toggleMenu()
|
||||
}}
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 text-sm font-medium text-gray-300 hover:bg-white/5 hover:text-white rounded-md transition-colors text-left"
|
||||
>
|
||||
{link.icon && (
|
||||
<link.icon size={16} strokeWidth={1.75} className="text-gray-400" />
|
||||
)}
|
||||
{link.name}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
<div className="pt-2 pb-1 border-t border-white/10 mt-1">
|
||||
) : (
|
||||
<button
|
||||
key={link.name}
|
||||
onClick={() => {
|
||||
link.action?.()
|
||||
toggleMenu()
|
||||
}}
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 text-sm font-medium text-gray-300 hover:bg-white/5 hover:text-white rounded-md transition-colors text-left"
|
||||
>
|
||||
{link.icon && (
|
||||
<link.icon size={16} strokeWidth={1.75} className="text-gray-400" />
|
||||
)}
|
||||
{link.name}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-1 border-t border-white/10 pt-2 pb-1">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<LanguageSelector />
|
||||
{themeToggle}
|
||||
<div className="flex h-10 min-w-10 items-center justify-center rounded-lg bg-white/5 ring-1 ring-white/10">
|
||||
<LanguageSelector />
|
||||
</div>
|
||||
<div className="flex h-10 items-center rounded-lg bg-white/5 px-1 ring-1 ring-white/10">
|
||||
{themeToggle}
|
||||
</div>
|
||||
{navLinks
|
||||
.filter((l) => isLoginLink(l.resourceKey))
|
||||
.map((link) =>
|
||||
link.path ? (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
onClick={toggleMenu}
|
||||
className="hidden h-10 lg:inline-flex items-center justify-center px-4 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shadow-md shadow-blue-900/30 transition-colors"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
) : null,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ export const ProductCard: React.FC<ProductCardProps> = ({
|
|||
<button
|
||||
onClick={handleAddToCart}
|
||||
disabled={isDisabled}
|
||||
className={`w-full font-medium py-3 px-4 rounded-lg transition-colors duration-200 transform ${
|
||||
className={`w-full font-medium py-3 px-4 rounded-lg transition-colors duration-200 transform dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white ${
|
||||
isDisabled
|
||||
? 'bg-gray-400 text-gray-700 cursor-not-allowed dark:bg-gray-700 dark:text-gray-400'
|
||||
: 'bg-blue-600 hover:bg-blue-700 text-white hover:scale-[1.02] active:scale-[0.98]'
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
|
|||
const { className, onError, onExpire, onSuccess } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Turnstile
|
||||
ref={ref}
|
||||
className={className ?? 'mb-4 mx-auto'}
|
||||
|
|
@ -21,7 +20,6 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
|
|||
onExpire={onExpire}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
Captcha.displayName = 'Captcha'
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ function DbMigrateLogPanel({ onClose }: DbMigrateLogPanelProps) {
|
|||
<div className="flex-1 overflow-y-auto p-3 font-mono text-xs leading-relaxed">
|
||||
{logs.map((log, i) => (
|
||||
<div key={i} className={`whitespace-pre-wrap break-words ${levelColor(log.level)}`}>
|
||||
<span className="text-gray-500 mr-2 select-none">[{log.level}]</span>
|
||||
<span className="text-gray-500 mr-2 select-none">{String(i + 1).padStart(4, '0')}</span>
|
||||
{log.message}
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
148
ui/src/components/shared/TenantSelector.tsx
Normal file
148
ui/src/components/shared/TenantSelector.tsx
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import Input from '@/components/ui/Input'
|
||||
import { getTenantByNameDetail } from '@/services/tenant.service'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { defaultDomain, getSubdomain } from '@/utils/subdomain'
|
||||
import type { CSSProperties } from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
const hiddenTenantStyle: CSSProperties = {
|
||||
opacity: 0,
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
height: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
border: 'none',
|
||||
}
|
||||
|
||||
const TenantSelector = () => {
|
||||
const { translate } = useLocalization()
|
||||
const isMultiTenant = useStoreState((state) => state.abpConfig.config?.multiTenancy.isEnabled)
|
||||
const tenantName = useStoreState((state) => state.locale.currentTenantName)
|
||||
const { setTenantName } = useStoreActions((actions) => actions.locale)
|
||||
const { setTenant } = useStoreActions((actions) => actions.auth.tenant)
|
||||
const { setWarning } = useStoreActions((actions) => actions.base.messages)
|
||||
const requestIdRef = useRef(0)
|
||||
const lastRequestedTenantNameRef = useRef<string>()
|
||||
|
||||
const subDomainName = getSubdomain()
|
||||
const isSubdomainTenant = !!subDomainName && subDomainName !== defaultDomain
|
||||
const tenantStyle = isSubdomainTenant ? hiddenTenantStyle : undefined
|
||||
|
||||
const setWarningTimeout = useCallback(
|
||||
(message: string) => {
|
||||
setTimeout(() => {
|
||||
setWarning(message)
|
||||
}, 100)
|
||||
},
|
||||
[setWarning],
|
||||
)
|
||||
|
||||
const redirectToMainDomain = useCallback(
|
||||
(name: string) => {
|
||||
setTenantName(undefined)
|
||||
const parts = window.location.hostname.split('.')
|
||||
const mainDomain = parts.length >= 3 ? parts.slice(1).join('.') : window.location.hostname
|
||||
setWarningTimeout(
|
||||
`"${name}" kurumuna ait kayıt bulunamadı.\nAna sayfaya yönlendiriliyorsunuz...`,
|
||||
)
|
||||
setTimeout(() => {
|
||||
window.location.href = `${window.location.protocol}//${mainDomain}`
|
||||
}, 3000)
|
||||
},
|
||||
[setTenantName, setWarningTimeout],
|
||||
)
|
||||
|
||||
const fetchDataByName = useCallback(
|
||||
async (name: string, isSubdomain = false) => {
|
||||
if (!isSubdomain && name === lastRequestedTenantNameRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
lastRequestedTenantNameRef.current = name
|
||||
const requestId = requestIdRef.current + 1
|
||||
requestIdRef.current = requestId
|
||||
|
||||
if (name) {
|
||||
try {
|
||||
const response = await getTenantByNameDetail(name)
|
||||
|
||||
if (requestId !== requestIdRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
if (response.data) {
|
||||
setTenant({
|
||||
tenantId: response.data.id,
|
||||
tenantName: response.data.name,
|
||||
menuGroup: response.data.menuGroup,
|
||||
})
|
||||
} else {
|
||||
setTenant(undefined)
|
||||
if (isSubdomain) redirectToMainDomain(name)
|
||||
}
|
||||
} catch {
|
||||
if (requestId !== requestIdRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
setTenant(undefined)
|
||||
if (isSubdomain) redirectToMainDomain(name)
|
||||
}
|
||||
} else {
|
||||
setTenant(undefined)
|
||||
}
|
||||
},
|
||||
[redirectToMainDomain, setTenant],
|
||||
)
|
||||
|
||||
const handleTenantNameChange = (value: string) => {
|
||||
setTenantName(value)
|
||||
}
|
||||
|
||||
const handleTenantNameBlur = () => {
|
||||
if (subDomainName) {
|
||||
return
|
||||
}
|
||||
|
||||
fetchDataByName(tenantName || '')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMultiTenant) {
|
||||
setTenant(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
if (subDomainName) {
|
||||
setTenantName(subDomainName)
|
||||
fetchDataByName(subDomainName, true)
|
||||
}
|
||||
}, [fetchDataByName, isMultiTenant, setTenant, setTenantName, subDomainName])
|
||||
|
||||
if (!isMultiTenant) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="form-label mb-2" style={tenantStyle}>
|
||||
{translate('::Organization')}
|
||||
</label>
|
||||
<div className="mb-4">
|
||||
<Input
|
||||
placeholder={translate('::Organization')}
|
||||
value={tenantName ?? ''}
|
||||
onChange={(event) => handleTenantNameChange(event.target.value)}
|
||||
onBlur={handleTenantNameBlur}
|
||||
style={tenantStyle}
|
||||
aria-hidden={isSubdomainTenant ? 'true' : 'false'}
|
||||
autoFocus={!isSubdomainTenant}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TenantSelector
|
||||
|
|
@ -19,6 +19,7 @@ export { default as SegmentItemOption } from './SegmentItemOption'
|
|||
export { default as StickyFooter } from './StickyFooter'
|
||||
export { default as SvgIcon } from './SvgIcon'
|
||||
export { default as TableRowSkeleton } from './loaders/TableRowSkeleton'
|
||||
export { default as TenantSelector } from './TenantSelector'
|
||||
export { default as TextBlockSkeleton } from './loaders/TextBlockSkeleton'
|
||||
export { default as TextEllipsis } from './TextEllipsis'
|
||||
export { default as UsersAvatarGroup } from './UsersAvatarGroup'
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const Logo = (props: LogoProps) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('logo', 'my-1', className)}
|
||||
className={classNames('logo', 'my-2', className)}
|
||||
style={{
|
||||
...style,
|
||||
...{ width: logoWidth },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, forwardRef } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import useTimeout from '../hooks/useTimeout'
|
||||
import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa';
|
||||
import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa'
|
||||
import { motion } from 'framer-motion'
|
||||
import CloseButton from '../CloseButton'
|
||||
import StatusIcon from '../StatusIcon'
|
||||
|
|
@ -9,173 +9,153 @@ import type { TypeAttributes, CommonProps } from '../@types/common'
|
|||
import type { ReactNode, MouseEvent } from 'react'
|
||||
|
||||
export interface AlertProps extends CommonProps {
|
||||
closable?: boolean
|
||||
customClose?: ReactNode | string
|
||||
customIcon?: ReactNode | string
|
||||
duration?: number
|
||||
title?: ReactNode | string
|
||||
onClose?: (e?: MouseEvent<HTMLDivElement>) => void
|
||||
rounded?: boolean
|
||||
showIcon?: boolean
|
||||
triggerByToast?: boolean
|
||||
type?: TypeAttributes.Status
|
||||
closable?: boolean
|
||||
customClose?: ReactNode | string
|
||||
customIcon?: ReactNode | string
|
||||
duration?: number
|
||||
title?: ReactNode | string
|
||||
onClose?: (e?: MouseEvent<HTMLDivElement>) => void
|
||||
rounded?: boolean
|
||||
showIcon?: boolean
|
||||
triggerByToast?: boolean
|
||||
type?: TypeAttributes.Status
|
||||
}
|
||||
|
||||
const DEFAULT_TYPE = 'warning'
|
||||
|
||||
const TYPE_MAP = {
|
||||
success: {
|
||||
backgroundColor: 'bg-emerald-50 dark:bg-emerald-500',
|
||||
titleColor: 'text-emerald-700 dark:text-emerald-50',
|
||||
textColor: 'text-emerald-500 dark:text-emerald-50',
|
||||
iconColor: 'text-emerald-400 dark:text-emerald-50',
|
||||
icon: <FaCheckCircle />,
|
||||
},
|
||||
info: {
|
||||
backgroundColor: 'bg-blue-50 dark:bg-blue-500',
|
||||
titleColor: 'text-blue-700 dark:text-blue-100',
|
||||
textColor: 'text-blue-500 dark:text-blue-100',
|
||||
iconColor: 'text-blue-400 dark:text-blue-100',
|
||||
icon: <FaInfoCircle />,
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: 'bg-yellow-50 dark:bg-yellow-500',
|
||||
titleColor: 'text-yellow-700 dark:text-yellow-50',
|
||||
textColor: 'text-yellow-500 dark:text-yellow-50',
|
||||
iconColor: 'text-yellow-400 dark:text-yellow-50',
|
||||
icon: <FaExclamation />,
|
||||
},
|
||||
danger: {
|
||||
backgroundColor: 'bg-red-50 dark:bg-red-500',
|
||||
titleColor: 'text-red-700 dark:text-red-100',
|
||||
textColor: 'text-red-500 dark:text-red-100',
|
||||
iconColor: 'text-red-400 dark:text-red-100',
|
||||
icon: <FaTimesCircle />,
|
||||
},
|
||||
success: {
|
||||
backgroundColor: 'bg-emerald-50 dark:bg-emerald-500',
|
||||
titleColor: 'text-emerald-700 dark:text-emerald-50',
|
||||
textColor: 'text-emerald-500 dark:text-emerald-50',
|
||||
iconColor: 'text-emerald-400 dark:text-emerald-50',
|
||||
icon: <FaCheckCircle />,
|
||||
},
|
||||
info: {
|
||||
backgroundColor: 'bg-blue-50 dark:bg-blue-500',
|
||||
titleColor: 'text-blue-700 dark:text-blue-100',
|
||||
textColor: 'text-blue-500 dark:text-blue-100',
|
||||
iconColor: 'text-blue-400 dark:text-blue-100',
|
||||
icon: <FaInfoCircle />,
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: 'bg-yellow-50 dark:bg-yellow-500',
|
||||
titleColor: 'text-yellow-700 dark:text-yellow-50',
|
||||
textColor: 'text-yellow-500 dark:text-yellow-50',
|
||||
iconColor: 'text-yellow-400 dark:text-yellow-50',
|
||||
icon: <FaExclamation />,
|
||||
},
|
||||
danger: {
|
||||
backgroundColor: 'bg-red-50 dark:bg-red-500',
|
||||
titleColor: 'text-red-700 dark:text-red-100',
|
||||
textColor: 'text-red-500 dark:text-red-100',
|
||||
iconColor: 'text-red-400 dark:text-red-100',
|
||||
icon: <FaTimesCircle />,
|
||||
},
|
||||
}
|
||||
|
||||
const TYPE_ARRAY: TypeAttributes.Status[] = [
|
||||
'success',
|
||||
'danger',
|
||||
'info',
|
||||
'warning',
|
||||
]
|
||||
const TYPE_ARRAY: TypeAttributes.Status[] = ['success', 'danger', 'info', 'warning']
|
||||
|
||||
const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
closable = false,
|
||||
customClose,
|
||||
customIcon,
|
||||
duration = 3000,
|
||||
title = null,
|
||||
onClose,
|
||||
rounded = true,
|
||||
showIcon = false,
|
||||
triggerByToast = false,
|
||||
...rest
|
||||
} = props
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
closable = false,
|
||||
customClose,
|
||||
customIcon,
|
||||
duration = 3000,
|
||||
title = null,
|
||||
onClose,
|
||||
rounded = true,
|
||||
showIcon = false,
|
||||
triggerByToast = false,
|
||||
...rest
|
||||
} = props
|
||||
|
||||
const getType = () => {
|
||||
const { type = DEFAULT_TYPE } = props
|
||||
if (TYPE_ARRAY.includes(type)) {
|
||||
return type
|
||||
}
|
||||
return DEFAULT_TYPE
|
||||
const getType = () => {
|
||||
const { type = DEFAULT_TYPE } = props
|
||||
if (TYPE_ARRAY.includes(type)) {
|
||||
return type
|
||||
}
|
||||
return DEFAULT_TYPE
|
||||
}
|
||||
|
||||
const type = getType()
|
||||
const typeMap = TYPE_MAP[type]
|
||||
const type = getType()
|
||||
const typeMap = TYPE_MAP[type]
|
||||
|
||||
const [display, setDisplay] = useState('show')
|
||||
const [display, setDisplay] = useState('show')
|
||||
|
||||
const { clear } = useTimeout(
|
||||
onClose as () => void,
|
||||
duration,
|
||||
(duration as number) > 0
|
||||
)
|
||||
const { clear } = useTimeout(onClose as () => void, duration, (duration as number) > 0)
|
||||
|
||||
const handleClose = (e: MouseEvent<HTMLDivElement>) => {
|
||||
setDisplay('hiding')
|
||||
onClose?.(e)
|
||||
clear()
|
||||
if (!triggerByToast) {
|
||||
setTimeout(() => {
|
||||
setDisplay('hide')
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
|
||||
const renderClose = () => {
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
role="presentation"
|
||||
onClick={(e) => handleClose(e)}
|
||||
>
|
||||
{customClose || <CloseButton defaultStyle={false} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const alertDefaultClass = 'p-2 relative flex'
|
||||
|
||||
const alertClass = classNames(
|
||||
'alert',
|
||||
alertDefaultClass,
|
||||
typeMap.backgroundColor,
|
||||
typeMap.textColor,
|
||||
!title ? 'font-semibold' : '',
|
||||
closable ? 'justify-between' : '',
|
||||
closable && !title ? 'items-center' : '',
|
||||
rounded && 'rounded-lg',
|
||||
className
|
||||
)
|
||||
|
||||
if (display === 'hide') {
|
||||
return null
|
||||
const handleClose = (e: MouseEvent<HTMLDivElement>) => {
|
||||
setDisplay('hiding')
|
||||
onClose?.(e)
|
||||
clear()
|
||||
if (!triggerByToast) {
|
||||
setTimeout(() => {
|
||||
setDisplay('hide')
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
|
||||
const renderClose = () => {
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
className={alertClass}
|
||||
initial={{ opacity: 1 }}
|
||||
animate={display === 'hiding' ? 'exit' : 'animate'}
|
||||
transition={{ duration: 0.25, type: 'tween' }}
|
||||
variants={{
|
||||
animate: {
|
||||
opacity: 1,
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
},
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<div className={`flex ${title ? '' : 'items-center'}`}>
|
||||
{showIcon && (
|
||||
<StatusIcon
|
||||
iconColor={typeMap.iconColor}
|
||||
custom={customIcon}
|
||||
type={type}
|
||||
/>
|
||||
)}
|
||||
<div className={showIcon ? 'ltr:ml-2 rtl:mr-2' : ''}>
|
||||
{title ? (
|
||||
<div
|
||||
className={`font-semibold mb-1 ${typeMap.titleColor}`}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{closable ? renderClose() : null}
|
||||
</motion.div>
|
||||
<div className="cursor-pointer" role="presentation" onClick={(e) => handleClose(e)}>
|
||||
{customClose || <CloseButton defaultStyle={false} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const alertDefaultClass = 'p-2 relative flex'
|
||||
|
||||
const alertClass = classNames(
|
||||
'alert',
|
||||
alertDefaultClass,
|
||||
typeMap.backgroundColor,
|
||||
typeMap.textColor,
|
||||
!title ? 'font-semibold' : '',
|
||||
closable ? 'justify-between' : '',
|
||||
closable && !title ? 'items-center' : '',
|
||||
rounded && 'rounded-lg',
|
||||
className,
|
||||
)
|
||||
|
||||
if (display === 'hide') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
className={alertClass}
|
||||
initial={{ opacity: 1 }}
|
||||
animate={display === 'hiding' ? 'exit' : 'animate'}
|
||||
transition={{ duration: 0.25, type: 'tween' }}
|
||||
variants={{
|
||||
animate: {
|
||||
opacity: 1,
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
},
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<div className={`flex min-w-0 ${title ? '' : 'items-center'}`}>
|
||||
{showIcon && <StatusIcon iconColor={typeMap.iconColor} custom={customIcon} type={type} />}
|
||||
<div
|
||||
className={classNames(
|
||||
'min-w-0 flex-1 whitespace-normal break-words leading-6',
|
||||
showIcon ? 'ltr:ml-2 rtl:mr-2' : '',
|
||||
)}
|
||||
>
|
||||
{title ? <div className={`font-semibold mb-1 ${typeMap.titleColor}`}>{title}</div> : null}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{closable ? renderClose() : null}
|
||||
</motion.div>
|
||||
)
|
||||
})
|
||||
|
||||
Alert.displayName = 'Alert'
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export interface ListFormWizardColumnItemDto {
|
|||
editorScript: string
|
||||
colSpan: number
|
||||
isRequired: boolean
|
||||
includeInEditingForm: boolean
|
||||
dbSourceType: number
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ export interface ListFormWizardDto {
|
|||
languageTextMenuParentTr: string
|
||||
permissionGroupName: string
|
||||
menuParentCode: string
|
||||
menuParentIcon?: string
|
||||
menuIcon: string
|
||||
dataSourceCode: string
|
||||
dataSourceConnectionString: string
|
||||
|
|
@ -113,6 +115,7 @@ export interface WizardSeedFileItemDto {
|
|||
editorScript: string
|
||||
colSpan: number
|
||||
isRequired: boolean
|
||||
includeInEditingForm?: boolean
|
||||
dbSourceType: number
|
||||
turkishCaption?: string
|
||||
englishCaption?: string
|
||||
|
|
|
|||
|
|
@ -908,6 +908,7 @@ export interface WidgetEditDto {
|
|||
|
||||
export interface WorkflowDto {
|
||||
approvalUserFieldName: string
|
||||
isFilterUserName: boolean
|
||||
approvalDateFieldName: string
|
||||
approvalStatusFieldName: string
|
||||
approvalDescriptionFieldName: string
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export interface RouteDto extends FullAuditedEntityDto<string> {
|
|||
id: string;
|
||||
key: string;
|
||||
path: string;
|
||||
componentType: string;
|
||||
componentPath: string;
|
||||
routeType: string;
|
||||
authority: string[];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { RouteDto } from '@/proxy/routes/models'
|
||||
import { lazy } from 'react'
|
||||
import { useComponents } from '@/contexts/ComponentContext'
|
||||
|
||||
// Tüm view bileşenlerini import et (vite özel)
|
||||
// shared klasörü hariç, çünkü bu bileşenler genellikle başka yerlerde statik import ediliyor
|
||||
|
|
@ -8,25 +7,12 @@ const modules = import.meta.glob(['../views/**/*.tsx', '!../views/shared/**/*.ts
|
|||
|
||||
const lazyComponentCache = new Map<string, React.LazyExoticComponent<React.ComponentType<any>>>()
|
||||
|
||||
// ComponentPath'in fiziksel mi yoksa dinamik mi olduğunu belirle
|
||||
function isPhysicalComponent(componentPath: string): boolean {
|
||||
// @ ile başlayan path'ler fiziksel dosya yolu
|
||||
// Başka bir kural: dynamic: ile başlayan path'ler dinamik
|
||||
return componentPath.startsWith('@/') || componentPath.startsWith('../')
|
||||
}
|
||||
|
||||
function isDynamicComponent(componentPath: string): boolean {
|
||||
// dynamic: ile başlayan path'ler dinamik komponent
|
||||
return componentPath.startsWith('dynamic:')
|
||||
}
|
||||
|
||||
// Fiziksel komponent yükleme (mevcut mantık)
|
||||
function loadPhysicalComponent(componentPath: string) {
|
||||
const cleanedPath = componentPath.replace(/^@\//, '')
|
||||
const fullPath = `../${cleanedPath}.tsx`
|
||||
|
||||
if (lazyComponentCache.has(fullPath)) {
|
||||
// console.log(`Physical component loaded from cache: ${fullPath}`)
|
||||
return lazyComponentCache.get(fullPath)!
|
||||
}
|
||||
|
||||
|
|
@ -43,53 +29,65 @@ function loadPhysicalComponent(componentPath: string) {
|
|||
|
||||
// Dinamik komponent yükleme (yeni mantık)
|
||||
function loadDynamicComponent(
|
||||
componentPath: string,
|
||||
componentPath: string,
|
||||
registeredComponents: Record<string, React.ComponentType<unknown>>,
|
||||
renderComponent?: (name: string, props?: any) => React.ReactNode,
|
||||
isComponentRegistered?: (name: string) => boolean
|
||||
isComponentRegistered?: (name: string) => boolean,
|
||||
) {
|
||||
const componentName = componentPath.replace('dynamic:', '')
|
||||
|
||||
|
||||
if (lazyComponentCache.has(componentPath)) {
|
||||
// console.log(`Dynamic component loaded from cache: ${componentName}`)
|
||||
return lazyComponentCache.get(componentPath)!
|
||||
}
|
||||
|
||||
// Önce manuel registered komponentleri kontrol et
|
||||
let DynamicComponent = registeredComponents[componentName]
|
||||
|
||||
let DynamicComponent = registeredComponents[componentPath]
|
||||
|
||||
// Eğer manuel registered'da yoksa, database compiled komponentleri kontrol et
|
||||
if (!DynamicComponent && isComponentRegistered && renderComponent && isComponentRegistered(componentName)) {
|
||||
DynamicComponent = (props: any) => renderComponent(componentName, props) as React.ReactElement
|
||||
if (
|
||||
!DynamicComponent &&
|
||||
isComponentRegistered &&
|
||||
renderComponent &&
|
||||
isComponentRegistered(componentPath)
|
||||
) {
|
||||
DynamicComponent = (props: any) => renderComponent(componentPath, props) as React.ReactElement
|
||||
}
|
||||
|
||||
if (!DynamicComponent) {
|
||||
if (isComponentRegistered) {
|
||||
console.log('Database component registry available - checking...')
|
||||
}
|
||||
throw new Error(`Dynamic component not found: ${componentName}`)
|
||||
throw new Error(`Dynamic component not found: ${componentPath}`)
|
||||
}
|
||||
|
||||
// console.log(`Dynamic component loaded: ${componentName}`)
|
||||
// console.log(`Dynamic component loaded: ${componentPath}`)
|
||||
// Dinamik komponent için lazy wrapper oluştur
|
||||
const LazyComponent = lazy(() => Promise.resolve({ default: DynamicComponent as React.ComponentType<any> }))
|
||||
const LazyComponent = lazy(() =>
|
||||
Promise.resolve({ default: DynamicComponent as React.ComponentType<any> }),
|
||||
)
|
||||
lazyComponentCache.set(componentPath, LazyComponent)
|
||||
return LazyComponent
|
||||
}
|
||||
|
||||
export function loadComponent(
|
||||
componentPath: string,
|
||||
componentType: string,
|
||||
componentPath: string,
|
||||
registeredComponents?: Record<string, React.ComponentType<unknown>>,
|
||||
renderComponent?: (name: string, props?: any) => React.ReactNode,
|
||||
isComponentRegistered?: (name: string) => boolean
|
||||
isComponentRegistered?: (name: string) => boolean,
|
||||
) {
|
||||
if (isPhysicalComponent(componentPath)) {
|
||||
if (componentType === 'normal') {
|
||||
return loadPhysicalComponent(componentPath)
|
||||
} else if (isDynamicComponent(componentPath)) {
|
||||
} else if (componentType === 'dynamic') {
|
||||
if (!registeredComponents) {
|
||||
throw new Error('Registered components required for dynamic component loading')
|
||||
}
|
||||
return loadDynamicComponent(componentPath, registeredComponents, renderComponent, isComponentRegistered)
|
||||
return loadDynamicComponent(
|
||||
componentPath,
|
||||
registeredComponents,
|
||||
renderComponent,
|
||||
isComponentRegistered,
|
||||
)
|
||||
} else {
|
||||
// Backward compatibility: varsayılan olarak fiziksel komponent kabul et
|
||||
return loadPhysicalComponent(componentPath)
|
||||
|
|
@ -103,13 +101,12 @@ export interface DynamicReactRoute {
|
|||
getComponent: (
|
||||
registeredComponents?: Record<string, React.ComponentType<unknown>>,
|
||||
renderComponent?: (name: string, props?: any) => React.ReactNode,
|
||||
isComponentRegistered?: (name: string) => boolean
|
||||
isComponentRegistered?: (name: string) => boolean,
|
||||
) => React.LazyExoticComponent<React.ComponentType<any>>
|
||||
routeType: string
|
||||
authority?: string[]
|
||||
componentType: string
|
||||
componentPath: string
|
||||
isPhysical: boolean
|
||||
isDynamic: boolean
|
||||
}
|
||||
|
||||
// API'den gelen route objesini, React Router için uygun hale getirir
|
||||
|
|
@ -117,12 +114,17 @@ export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] {
|
|||
return routes.map((route) => ({
|
||||
key: route.path,
|
||||
path: route.path,
|
||||
getComponent: (registeredComponents, renderComponent, isComponentRegistered) =>
|
||||
loadComponent(route.componentPath, registeredComponents, renderComponent, isComponentRegistered),
|
||||
getComponent: (registeredComponents, renderComponent, isComponentRegistered) =>
|
||||
loadComponent(
|
||||
route.componentType,
|
||||
route.componentPath,
|
||||
registeredComponents,
|
||||
renderComponent,
|
||||
isComponentRegistered,
|
||||
),
|
||||
routeType: route.routeType,
|
||||
authority: route.authority,
|
||||
componentType: route.componentType,
|
||||
componentPath: route.componentPath,
|
||||
isPhysical: isPhysicalComponent(route.componentPath),
|
||||
isDynamic: isDynamicComponent(route.componentPath),
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,42 @@ const AccessDenied = React.lazy(() => import('@/views/AccessDenied'))
|
|||
const NotFound = React.lazy(() => import('@/views/NotFound'))
|
||||
const DatabaseSetup = React.lazy(() => import('@/views/setup/DatabaseSetup'))
|
||||
|
||||
const RootRedirect = () => {
|
||||
const location = useLocation()
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
const isPasswordResetLink = searchParams.has('userId') && searchParams.has('resetToken')
|
||||
|
||||
return (
|
||||
<Navigate
|
||||
to={
|
||||
isPasswordResetLink
|
||||
? `${ROUTES_ENUM.authenticated.resetPassword}${location.search}`
|
||||
: hasSubdomain()
|
||||
? ROUTES_ENUM.authenticated.login
|
||||
: ROUTES_ENUM.public.home
|
||||
}
|
||||
replace
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const LegacyPasswordResetRedirect = () => {
|
||||
const location = useLocation()
|
||||
|
||||
return <Navigate to={`${ROUTES_ENUM.authenticated.resetPassword}${location.search}`} replace />
|
||||
}
|
||||
|
||||
const LegacyEmailConfirmationRedirect = () => {
|
||||
const location = useLocation()
|
||||
|
||||
return <Navigate to={location.pathname.replace(/^\/account/, '')} replace />
|
||||
}
|
||||
|
||||
export const DynamicRouter: React.FC = () => {
|
||||
const { routes, loading, error } = useDynamicRoutes()
|
||||
const { registeredComponents, renderComponent, isComponentRegistered } = useComponents()
|
||||
const location = useLocation()
|
||||
|
||||
|
||||
const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
|
||||
|
||||
// /setup path'inde loading bekleme — setup route her zaman erişilebilir olmalı
|
||||
|
|
@ -33,7 +64,11 @@ export const DynamicRouter: React.FC = () => {
|
|||
{dynamicRoutes
|
||||
.filter((r) => r.routeType === 'protected')
|
||||
.map((route) => {
|
||||
const Component = route.getComponent(registeredComponents, renderComponent, isComponentRegistered)
|
||||
const Component = route.getComponent(
|
||||
registeredComponents,
|
||||
renderComponent,
|
||||
isComponentRegistered,
|
||||
)
|
||||
return (
|
||||
<Route
|
||||
key={route.key}
|
||||
|
|
@ -85,7 +120,11 @@ export const DynamicRouter: React.FC = () => {
|
|||
hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected',
|
||||
)
|
||||
.map((route) => {
|
||||
const Component = route.getComponent(registeredComponents, renderComponent, isComponentRegistered)
|
||||
const Component = route.getComponent(
|
||||
registeredComponents,
|
||||
renderComponent,
|
||||
isComponentRegistered,
|
||||
)
|
||||
return (
|
||||
<Route
|
||||
key={route.key}
|
||||
|
|
@ -100,15 +139,9 @@ export const DynamicRouter: React.FC = () => {
|
|||
})}
|
||||
|
||||
{/* root redirect */}
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<Navigate
|
||||
to={hasSubdomain() ? ROUTES_ENUM.authenticated.login : ROUTES_ENUM.public.home}
|
||||
replace
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/" element={<RootRedirect />} />
|
||||
<Route path="/account/confirm/:userId/:token" element={<LegacyEmailConfirmationRedirect />} />
|
||||
<Route path="/account/reset-password" element={<LegacyPasswordResetRedirect />} />
|
||||
|
||||
{/* public access denied (statik) */}
|
||||
<Route
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ export const resetPassword = (userId: string, resetToken: string, password: stri
|
|||
},
|
||||
})
|
||||
|
||||
export const sendAccountConfirmationCode = (data: any) => {
|
||||
apiService.fetchData({
|
||||
export const sendAccountConfirmationCode = (data: any) =>
|
||||
apiService.fetchData<boolean>({
|
||||
method: 'POST',
|
||||
url: 'api/app/platform-account/send-account-confirmation-code',
|
||||
data: {
|
||||
|
|
@ -61,7 +61,6 @@ export const sendAccountConfirmationCode = (data: any) => {
|
|||
captchaResponse: data.captchaResponse,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const verifyAccountConfirmationCode = (userId: string, token: string) =>
|
||||
apiService.fetchData({
|
||||
|
|
@ -74,7 +73,7 @@ export const verifyAccountConfirmationCode = (userId: string, token: string) =>
|
|||
})
|
||||
|
||||
export const sendExtendLoginRequest = (data: any) =>
|
||||
apiService.fetchData({
|
||||
apiService.fetchData<boolean>({
|
||||
method: 'POST',
|
||||
url: 'api/app/platform-account/send-extend-login-request',
|
||||
data: {
|
||||
|
|
|
|||
24
ui/src/services/auditLog.service.ts
Normal file
24
ui/src/services/auditLog.service.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { PagedResultDto } from '@/proxy'
|
||||
import { AuditLogDto } from '@/proxy/auditLog/audit-log'
|
||||
import apiService from '@/services/api.service'
|
||||
|
||||
export interface AuditLogListRequestDto {
|
||||
skipCount?: number
|
||||
maxResultCount?: number
|
||||
sorting?: string
|
||||
listFormCode?: string
|
||||
entityId?: string
|
||||
}
|
||||
|
||||
class AuditLogService {
|
||||
async getList(params?: AuditLogListRequestDto): Promise<PagedResultDto<AuditLogDto>> {
|
||||
const response = await apiService.fetchData<PagedResultDto<AuditLogDto>>({
|
||||
url: '/api/app/audit-log',
|
||||
method: 'GET',
|
||||
params,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
export const auditLogService = new AuditLogService()
|
||||
|
|
@ -11,6 +11,11 @@ import { ListResultDto, PagedAndSortedResultRequestDto, PagedResultDto } from '.
|
|||
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
||||
import apiService from './api.service'
|
||||
|
||||
export interface UserAvatarUpdateInput {
|
||||
userId: string
|
||||
avatar?: File
|
||||
}
|
||||
|
||||
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
||||
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
||||
method: 'GET',
|
||||
|
|
@ -36,6 +41,21 @@ export const putUserDetail = (input: UserInfoViewModel) =>
|
|||
data: input,
|
||||
})
|
||||
|
||||
export const putUserAvatar = (input: UserAvatarUpdateInput) => {
|
||||
const formData = new FormData()
|
||||
formData.append('userId', input.userId)
|
||||
|
||||
if (input.avatar) {
|
||||
formData.append('avatar', input.avatar)
|
||||
}
|
||||
|
||||
return apiService.fetchData({
|
||||
method: 'PUT',
|
||||
url: `/api/app/platform-identity/avatar`,
|
||||
data: formData,
|
||||
})
|
||||
}
|
||||
|
||||
export const putUserLookout = (input: UserInfoViewModel) =>
|
||||
apiService.fetchData({
|
||||
method: 'PUT',
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export interface WorkflowRunResultDto {
|
|||
currentNodeKind?: string | null
|
||||
waitingApproval: boolean
|
||||
completed: boolean
|
||||
toastMessages?: string[]
|
||||
}
|
||||
|
||||
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {
|
||||
|
|
|
|||
29
ui/src/utils/editorScriptRuntime.ts
Normal file
29
ui/src/utils/editorScriptRuntime.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
export type EditorScriptRuntimeContext = {
|
||||
formData: Record<string, any>
|
||||
e: any
|
||||
editor: any
|
||||
runtimeSetEditorReadOnly?: (field: string, readOnly: boolean) => void
|
||||
setFormData?: (newData: any) => void
|
||||
}
|
||||
|
||||
export const executeEditorScript = (
|
||||
script: string,
|
||||
{
|
||||
formData,
|
||||
e,
|
||||
editor,
|
||||
runtimeSetEditorReadOnly,
|
||||
setFormData,
|
||||
}: EditorScriptRuntimeContext,
|
||||
) => {
|
||||
const executor = new Function(
|
||||
'formData',
|
||||
'e',
|
||||
'editor',
|
||||
'runtimeSetEditorReadOnly',
|
||||
'setFormData',
|
||||
script,
|
||||
)
|
||||
|
||||
return executor(formData, e, editor, runtimeSetEditorReadOnly, setFormData)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import {
|
||||
sendAccountConfirmationCode,
|
||||
verifyAccountConfirmationCode,
|
||||
|
|
@ -12,15 +13,28 @@ function useAccount() {
|
|||
|
||||
const sendConfirmationCode = async (values: any) => {
|
||||
try {
|
||||
await sendAccountConfirmationCode(values)
|
||||
const result = await sendAccountConfirmationCode(values)
|
||||
if (result.data !== true) {
|
||||
throw new Error('This email is already confirmed or no account was found.')
|
||||
}
|
||||
|
||||
setError('')
|
||||
setMessage('Verification code has been sent to your e-mail address.')
|
||||
return {
|
||||
status: 'success',
|
||||
}
|
||||
} catch (error: any) {
|
||||
const err =
|
||||
error?.response?.data?.error?.message ||
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
error.toString()
|
||||
|
||||
setMessage('')
|
||||
setError(error?.response?.data?.message || error.toString())
|
||||
setError(err)
|
||||
return {
|
||||
status: 'failed',
|
||||
message: error?.response?.data?.message || error.toString(),
|
||||
message: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +52,7 @@ function useAccount() {
|
|||
setError('')
|
||||
setMessage('Your account is confirmed')
|
||||
setTimeout(() => {
|
||||
navigate('/account/login')
|
||||
navigate(ROUTES_ENUM.authenticated.login)
|
||||
}, 3000)
|
||||
} else {
|
||||
throw new Error('Invalid token')
|
||||
|
|
|
|||
|
|
@ -328,6 +328,91 @@ export function emptyCriteria(kind = 'Compare', listFormCode = ''): WorkflowCrit
|
|||
}
|
||||
}
|
||||
|
||||
export function uniqueCriteriaTitle(
|
||||
kind: string,
|
||||
criteria: Array<Pick<WorkflowCriteriaDto, 'id' | 'kind' | 'title'>>,
|
||||
currentId?: string | null,
|
||||
preferredTitle?: string | null,
|
||||
) {
|
||||
const hasPreferredTitle = Boolean(preferredTitle?.trim())
|
||||
const baseTitle = (preferredTitle || defaultTitle(kind)).trim()
|
||||
const usedTitles = new Set(
|
||||
criteria
|
||||
.filter((item) => !currentId || item.id !== currentId)
|
||||
.map((item) => (item.title || '').trim().toLocaleLowerCase('tr-TR'))
|
||||
.filter(Boolean),
|
||||
)
|
||||
|
||||
if (!hasPreferredTitle) {
|
||||
const sameKindCount = criteria.filter(
|
||||
(item) =>
|
||||
(!currentId || item.id !== currentId) &&
|
||||
item.kind === kind &&
|
||||
isDefaultTitleVariant(item.title, baseTitle),
|
||||
).length
|
||||
let index = sameKindCount + 1
|
||||
let candidate = `${baseTitle}${index}`
|
||||
while (usedTitles.has(candidate.toLocaleLowerCase('tr-TR'))) {
|
||||
index += 1
|
||||
candidate = `${baseTitle}${index}`
|
||||
}
|
||||
|
||||
return candidate
|
||||
}
|
||||
|
||||
if (!usedTitles.has(baseTitle.toLocaleLowerCase('tr-TR'))) {
|
||||
return baseTitle
|
||||
}
|
||||
|
||||
let index = 1
|
||||
let candidate = `${baseTitle}${index}`
|
||||
while (usedTitles.has(candidate.toLocaleLowerCase('tr-TR'))) {
|
||||
index += 1
|
||||
candidate = `${baseTitle}${index}`
|
||||
}
|
||||
|
||||
return candidate
|
||||
}
|
||||
|
||||
export function uniqueCriteriaId(
|
||||
criteria: Array<Pick<WorkflowCriteriaDto, 'id'>>,
|
||||
reservedIds: string[] = [],
|
||||
) {
|
||||
const usedIds = new Set(
|
||||
[...criteria.map((item) => item.id), ...reservedIds]
|
||||
.map((id) => (id || '').trim().toLocaleLowerCase('tr-TR'))
|
||||
.filter(Boolean),
|
||||
)
|
||||
const maxNumber = [...usedIds].reduce((max, id) => Math.max(max, parseCriteriaIdNumber(id)), 0)
|
||||
let nextNumber = maxNumber + 1
|
||||
let candidate = formatCriteriaId(nextNumber)
|
||||
|
||||
while (usedIds.has(candidate.toLocaleLowerCase('tr-TR'))) {
|
||||
nextNumber += 1
|
||||
candidate = formatCriteriaId(nextNumber)
|
||||
}
|
||||
|
||||
return candidate
|
||||
}
|
||||
|
||||
function parseCriteriaIdNumber(id: string) {
|
||||
const match = id.match(/^(?:n)?(\d+)$/iu)
|
||||
return match ? Number(match[1]) : 0
|
||||
}
|
||||
|
||||
function formatCriteriaId(number: number) {
|
||||
return `N${String(number).padStart(3, '0')}`.slice(-4)
|
||||
}
|
||||
|
||||
function isDefaultTitleVariant(title: string | null | undefined, baseTitle: string) {
|
||||
const normalized = (title || '').trim()
|
||||
return normalized === baseTitle || new RegExp(`^${escapeRegExp(baseTitle)}\\d+$`, 'u').test(normalized)
|
||||
}
|
||||
|
||||
function escapeRegExp(value: string) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm {
|
||||
const sharedPerson = item.approver || ''
|
||||
|
||||
|
|
@ -347,7 +432,8 @@ export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm
|
|||
}
|
||||
|
||||
export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput {
|
||||
const sharedPerson = item.approver || ''
|
||||
const sharedPerson =
|
||||
item.kind === 'Approval' || item.kind === 'Inform' ? item.approver || '' : ''
|
||||
const compareOutcomes = (item.compareOutcomes || [])
|
||||
.slice(0, 4)
|
||||
.filter((outcome) => outcome.label?.trim())
|
||||
|
|
@ -504,7 +590,7 @@ export function criteriaSummary(item: WorkflowCriteriaDto) {
|
|||
if (item.kind === 'Approval' || item.kind === 'Inform') {
|
||||
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
|
||||
}
|
||||
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
|
||||
return item.title
|
||||
}
|
||||
|
||||
export function targetTitle(criteria: WorkflowCriteriaDto[], id?: string | null) {
|
||||
|
|
|
|||
|
|
@ -46,9 +46,11 @@ const Views = (props: ViewsProps) => {
|
|||
<ErrorBoundary fallbackRender={fallbackRender}>
|
||||
<Suspense fallback={<Loading loading={true} />}>
|
||||
{!!warning?.length && (
|
||||
<Alert showIcon className="mb-4" type="warning">
|
||||
<Alert showIcon className="mb-4 text-sm text-left" type="warning">
|
||||
{warning.map((w, i) => (
|
||||
<div key={i}>{w}</div>
|
||||
<div key={i} className="whitespace-normal break-words">
|
||||
{w}
|
||||
</div>
|
||||
))}
|
||||
</Alert>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -456,13 +456,15 @@ function OrgChartNode({
|
|||
style={{ cursor: dragging ? 'grabbing' : 'grab' }}
|
||||
>
|
||||
{/* Header bar */}
|
||||
<div data-header="" className={`${headerBg} rounded-t-xl px-3 py-2 flex items-center gap-2`}>
|
||||
<div data-header="" className={`${headerBg} rounded-t-xl px-3 py-2 flex items-center gap-2 dark:bg-gray-900 dark:text-gray-100`}>
|
||||
{mode === 'department' ? (
|
||||
<FaBuilding className="w-3 h-3 text-white opacity-80 flex-shrink-0" />
|
||||
) : (
|
||||
<FaBriefcase className="w-3 h-3 text-white opacity-80 flex-shrink-0" />
|
||||
)}
|
||||
<span data-node-name="" className="text-white font-semibold text-xs truncate flex-1">{node.name}</span>
|
||||
<span data-node-name="" className="text-white font-semibold text-xs truncate flex-1 dark:bg-gray-900 dark:text-gray-100">
|
||||
{node.name}
|
||||
</span>
|
||||
{hasChildren && (
|
||||
<button
|
||||
data-stop-drag="true"
|
||||
|
|
@ -514,7 +516,7 @@ function OrgChartNode({
|
|||
|
||||
{/* Child count badge */}
|
||||
{hasChildren && (
|
||||
<div className="absolute -bottom-2.5 left-1/2 -translate-x-1/2 bg-white border border-slate-200 rounded-full px-2 py-0.5 text-xs text-slate-500 shadow-sm whitespace-nowrap z-10">
|
||||
<div className="absolute -bottom-2.5 left-1/2 -translate-x-1/2 bg-white border border-slate-200 rounded-full px-2 py-0.5 text-xs text-slate-500 shadow-sm whitespace-nowrap z-10 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-100">
|
||||
{node.children.length}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ import {
|
|||
emptyCriteria,
|
||||
normalizeCriteria,
|
||||
toCriteriaForm,
|
||||
uniqueCriteriaTitle,
|
||||
type WorkflowCriteriaForm,
|
||||
} from '@/utils/workflow/workflowHelpers'
|
||||
import { workflowService, type WorkflowCriteriaDto } from '@/services/workflow.service'
|
||||
import { WorkflowDesigner } from '../workflow/WorkflowDesigner'
|
||||
import { SelectBoxOption } from '@/types/shared'
|
||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||
import { Button, Card, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
||||
import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
||||
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
|
||||
import { object, string } from 'yup'
|
||||
import { bool, object, string } from 'yup'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { FormEditProps } from './FormEdit'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
|
@ -112,9 +113,17 @@ export function FormTabWorkflow(
|
|||
const saveCriteria = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault()
|
||||
runAction(async () => {
|
||||
const normalized = normalizeCriteria(criteriaForm)
|
||||
const preferredTitle = criteriaForm.title || normalized.title
|
||||
await workflowService.saveCriteria({
|
||||
...normalizeCriteria(criteriaForm),
|
||||
...normalized,
|
||||
listFormCode: props.listFormCode,
|
||||
title: uniqueCriteriaTitle(
|
||||
normalized.kind || '',
|
||||
currentCriteria,
|
||||
normalized.id,
|
||||
preferredTitle,
|
||||
),
|
||||
})
|
||||
setSelectedId('')
|
||||
})
|
||||
|
|
@ -123,9 +132,11 @@ export function FormTabWorkflow(
|
|||
const addCriteria = (kind: string) => {
|
||||
setDesignerTab('flow')
|
||||
runAction(async () => {
|
||||
const nextTitle = uniqueCriteriaTitle(kind, currentCriteria)
|
||||
const saved = await workflowService.saveCriteria({
|
||||
...normalizeCriteria(emptyCriteria(kind, props.listFormCode)),
|
||||
listFormCode: props.listFormCode,
|
||||
title: nextTitle,
|
||||
positionX: 80 + (currentCriteria.length % 5) * 230,
|
||||
positionY: 220 + Math.floor(currentCriteria.length / 5) * 140,
|
||||
})
|
||||
|
|
@ -296,10 +307,13 @@ export function FormTabWorkflow(
|
|||
}
|
||||
|
||||
const schema = object().shape({
|
||||
approvalUserFieldName: string().required(),
|
||||
approvalStatusFieldName: string().required(),
|
||||
approvalDateFieldName: string(),
|
||||
approvalDescriptionFieldName: string(),
|
||||
workflowDto: object().shape({
|
||||
approvalUserFieldName: string().required(),
|
||||
isFilterUserName: bool(),
|
||||
approvalStatusFieldName: string().required(),
|
||||
approvalDateFieldName: string(),
|
||||
approvalDescriptionFieldName: string(),
|
||||
}),
|
||||
})
|
||||
|
||||
const initialValues = useStoreState((s) => s.admin.lists.values)
|
||||
|
|
@ -320,7 +334,7 @@ export function FormTabWorkflow(
|
|||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<Card className="my-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-2">
|
||||
<FormItem
|
||||
asterisk
|
||||
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalUserFieldName')}
|
||||
|
|
@ -370,7 +384,7 @@ export function FormTabWorkflow(
|
|||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
|
||||
<FormItem
|
||||
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalDateFieldName')}
|
||||
invalid={
|
||||
|
|
@ -421,9 +435,22 @@ export function FormTabWorkflow(
|
|||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label={translate('::ListForms.ListFormEdit.Workflow.IsFilterUserName')}
|
||||
invalid={
|
||||
!!(
|
||||
errors.workflowDto?.isFilterUserName &&
|
||||
touched.workflowDto?.isFilterUserName
|
||||
)
|
||||
}
|
||||
errorMessage={errors.workflowDto?.isFilterUserName as string}
|
||||
>
|
||||
<Field name="workflowDto.isFilterUserName" component={Checkbox} />
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
<Button block variant="solid" loading={isSubmitting}>
|
||||
<Button block variant="solid" type="submit" loading={isSubmitting}>
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -24,16 +24,12 @@ const baseInputClass =
|
|||
const boolOptions = [
|
||||
{ key: 'showClearButton', label: 'showClearButton' },
|
||||
{ key: 'readOnly', label: 'readOnly' },
|
||||
{ key: 'disabled', label: 'disabled' },
|
||||
{ key: 'searchEnabled', label: 'searchEnabled' },
|
||||
{ key: 'showDataBeforeSearch', label: 'showDataBeforeSearch' },
|
||||
{ key: 'acceptCustomValue', label: 'acceptCustomValue' },
|
||||
{ key: 'showSpinButtons', label: 'showSpinButtons' },
|
||||
{ key: 'useMaskBehavior', label: 'useMaskBehavior' },
|
||||
{ key: 'useMaskedValue', label: 'useMaskedValue' },
|
||||
{ key: 'spellcheck', label: 'spellcheck' },
|
||||
{ key: 'openOnFieldClick', label: 'openOnFieldClick' },
|
||||
{ key: 'showDropDownButton', label: 'showDropDownButton' },
|
||||
]
|
||||
|
||||
const htmlToolbarItems = [
|
||||
|
|
@ -302,10 +298,6 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
Editor Options Builder
|
||||
</h5>
|
||||
<FaSlidersH
|
||||
className="text-gray-400"
|
||||
title="Bu dialog editorOptions alanını metin yazmadan oluşturmak için kullanılır."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{parseError && (
|
||||
|
|
@ -314,8 +306,8 @@ function EditorOptionsBuilderDialog({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid min-h-0 grid-cols-1 gap-4 overflow-y-auto pr-1 lg:grid-cols-3">
|
||||
<section className="lg:col-span-2 flex flex-col gap-4">
|
||||
<div className="grid grid-cols-2 min-h-0 gap-4 overflow-y-auto pr-1 ">
|
||||
<section className="flex flex-col gap-4">
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div
|
||||
className="mb-3 text-sm font-semibold"
|
||||
|
|
@ -327,7 +319,7 @@ function EditorOptionsBuilderDialog({
|
|||
{boolOptions.map((option) => (
|
||||
<label
|
||||
key={option.key}
|
||||
className="flex items-center gap-2 rounded border border-gray-100 dark:border-gray-700 px-2 py-2 text-sm"
|
||||
className="flex items-center gap-2 rounded px-1 py-1 text-sm"
|
||||
title={`${option.key}: true/false olarak editorOptions içine yazılır.`}
|
||||
>
|
||||
<input
|
||||
|
|
@ -565,7 +557,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
5. NumberBox
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||
{['min', 'max', 'step'].map((key) => (
|
||||
<label key={key} className="text-xs text-gray-500">
|
||||
{key}
|
||||
|
|
@ -585,7 +577,7 @@ function EditorOptionsBuilderDialog({
|
|||
</label>
|
||||
))}
|
||||
<label className="text-xs text-gray-500">
|
||||
format.type
|
||||
type
|
||||
<select
|
||||
className={baseInputClass}
|
||||
value={toSelectValue(getByPath(options, 'format.type'))}
|
||||
|
|
@ -600,7 +592,7 @@ function EditorOptionsBuilderDialog({
|
|||
</select>
|
||||
</label>
|
||||
<label className="text-xs text-gray-500">
|
||||
format.precision
|
||||
precision
|
||||
<input
|
||||
className={baseInputClass}
|
||||
value={toInputValue(getByPath(options, 'format.precision'))}
|
||||
|
|
@ -706,12 +698,106 @@ function EditorOptionsBuilderDialog({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div className="flex items-center justify-between gap-2 mb-3">
|
||||
<div>
|
||||
<div
|
||||
className="text-sm font-semibold"
|
||||
title="DevExtreme dokümanındaki herhangi bir editorOptions path'ini eklemek için kullan. Örnek path: toolbar.multiline, maskRules.X, inputAttr.aria-label."
|
||||
>
|
||||
7. Özel Ayarlar
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
icon={<FaPlus />}
|
||||
onClick={() =>
|
||||
setCustomOptions((current) => [
|
||||
...current,
|
||||
{
|
||||
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||
path: '',
|
||||
value: '',
|
||||
type: 'string',
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
Ekle
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{customOptions.map((option) => (
|
||||
<div key={option.id} className="grid grid-cols-12 gap-2">
|
||||
<input
|
||||
className={`${baseInputClass} col-span-4`}
|
||||
value={option.path}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id ? { ...item, path: event.target.value } : item,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="path.to.option"
|
||||
title="Nokta ile nested path yaz. Örnek: toolbar.multiline"
|
||||
/>
|
||||
<select
|
||||
className={`${baseInputClass} col-span-2`}
|
||||
value={option.type}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id
|
||||
? { ...item, type: event.target.value as CustomOption['type'] }
|
||||
: item,
|
||||
),
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="string">string</option>
|
||||
<option value="number">number</option>
|
||||
<option value="boolean">boolean</option>
|
||||
<option value="json">json</option>
|
||||
</select>
|
||||
<input
|
||||
className={`${baseInputClass} col-span-5`}
|
||||
value={option.value}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id ? { ...item, value: event.target.value } : item,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder={option.type === 'boolean' ? 'true / false' : 'value'}
|
||||
title="Tip JSON ise object/array yazabilirsin. Tip boolean ise true veya false yaz."
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
icon={<FaTrash />}
|
||||
onClick={() =>
|
||||
setCustomOptions((current) =>
|
||||
current.filter((item) => item.id !== option.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div
|
||||
className="mb-3 text-sm font-semibold"
|
||||
title="Daha önce kullandığın hazır editorOptions örnekleri. Tıkladığın preset mevcut JSON ile birleştirilir, yani başka seçenekleri silmeden üzerine ekler."
|
||||
>
|
||||
7. Hazır Ayarlar
|
||||
Hazır Ayarlar
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
|
|
@ -886,108 +972,14 @@ function EditorOptionsBuilderDialog({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div className="flex items-center justify-between gap-2 mb-3">
|
||||
<div>
|
||||
<div
|
||||
className="text-sm font-semibold"
|
||||
title="DevExtreme dokümanındaki herhangi bir editorOptions path'ini eklemek için kullan. Örnek path: toolbar.multiline, maskRules.X, inputAttr.aria-label."
|
||||
>
|
||||
8. Özel Ayarlar
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
icon={<FaPlus />}
|
||||
onClick={() =>
|
||||
setCustomOptions((current) => [
|
||||
...current,
|
||||
{
|
||||
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||
path: '',
|
||||
value: '',
|
||||
type: 'string',
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
Ekle
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{customOptions.map((option) => (
|
||||
<div key={option.id} className="grid grid-cols-12 gap-2">
|
||||
<input
|
||||
className={`${baseInputClass} col-span-4`}
|
||||
value={option.path}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id ? { ...item, path: event.target.value } : item,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="path.to.option"
|
||||
title="Nokta ile nested path yaz. Örnek: toolbar.multiline"
|
||||
/>
|
||||
<select
|
||||
className={`${baseInputClass} col-span-2`}
|
||||
value={option.type}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id
|
||||
? { ...item, type: event.target.value as CustomOption['type'] }
|
||||
: item,
|
||||
),
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="string">string</option>
|
||||
<option value="number">number</option>
|
||||
<option value="boolean">boolean</option>
|
||||
<option value="json">json</option>
|
||||
</select>
|
||||
<input
|
||||
className={`${baseInputClass} col-span-5`}
|
||||
value={option.value}
|
||||
onChange={(event) =>
|
||||
setCustomOptions((current) =>
|
||||
current.map((item) =>
|
||||
item.id === option.id ? { ...item, value: event.target.value } : item,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder={option.type === 'boolean' ? 'true / false' : 'value'}
|
||||
title="Tip JSON ise object/array yazabilirsin. Tip boolean ise true veya false yaz."
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
icon={<FaTrash />}
|
||||
onClick={() =>
|
||||
setCustomOptions((current) =>
|
||||
current.filter((item) => item.id !== option.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
||||
<div
|
||||
className="flex items-center gap-2 text-sm font-semibold mb-3"
|
||||
className="flex items-center gap-2 text-sm font-semibold mt-3"
|
||||
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
||||
>
|
||||
<FaCode />
|
||||
JSON Önizleme
|
||||
</div>
|
||||
<pre className="flex-1 overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
||||
<pre className="flex overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
||||
{preview || '{}'}
|
||||
</pre>
|
||||
</section>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ import { ROUTES_ENUM } from '@/routes/route.constant'
|
|||
import { SelectBoxOption } from '@/types/shared'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Form, Formik, FormikProps } from 'formik'
|
||||
import type { KeyboardEvent } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
|
@ -70,6 +71,7 @@ const initialValues: ListFormWizardDto = {
|
|||
languageTextMenuParentTr: '',
|
||||
permissionGroupName: '',
|
||||
menuParentCode: '',
|
||||
menuParentIcon: '',
|
||||
menuIcon: '',
|
||||
dataSourceCode: '',
|
||||
dataSourceConnectionString: '',
|
||||
|
|
@ -94,6 +96,7 @@ const initialValues: ListFormWizardDto = {
|
|||
widgets: [],
|
||||
workflow: {
|
||||
approvalUserFieldName: '',
|
||||
isFilterUserName: false,
|
||||
approvalDateFieldName: '',
|
||||
approvalStatusFieldName: '',
|
||||
approvalDescriptionFieldName: '',
|
||||
|
|
@ -221,6 +224,7 @@ const Wizard = () => {
|
|||
const [widgets, setWidgets] = useState<WidgetEditDto[]>([])
|
||||
const [workflow, setWorkflow] = useState<WorkflowDto>({
|
||||
approvalUserFieldName: '',
|
||||
isFilterUserName: false,
|
||||
approvalDateFieldName: '',
|
||||
approvalStatusFieldName: '',
|
||||
approvalDescriptionFieldName: '',
|
||||
|
|
@ -240,6 +244,18 @@ const Wizard = () => {
|
|||
])
|
||||
|
||||
const isAuditColumn = (columnName: string) => AUDIT_COLUMNS.has(columnName.toLowerCase())
|
||||
const isTenantColumn = (columnName: string) => columnName.toLowerCase() === 'tenantid'
|
||||
const isAutoSelectedColumn = (columnName: string, isTenant = false) =>
|
||||
!isAuditColumn(columnName) && !(isTenant && isTenantColumn(columnName))
|
||||
|
||||
const removeTenantColumn = (columns: Set<string>) =>
|
||||
new Set([...columns].filter((columnName) => !isTenantColumn(columnName)))
|
||||
|
||||
const removeTenantGroupItems = (groups: WizardGroup[]) =>
|
||||
groups.map((group) => ({
|
||||
...group,
|
||||
items: group.items.filter((item) => !isTenantColumn(item.dataField)),
|
||||
}))
|
||||
|
||||
const loadColumns = async (dsCode: string, schema: string, name: string) => {
|
||||
if (!dsCode || !name) {
|
||||
|
|
@ -253,12 +269,13 @@ const Wizard = () => {
|
|||
const res = await sqlObjectManagerService.getTableColumns(dsCode, schema, name)
|
||||
const cols = res.data ?? []
|
||||
setSelectCommandColumns(cols)
|
||||
const selectableColumns = cols.filter((c) => !isAuditColumn(c.columnName))
|
||||
const colNames = new Set(cols.map((c) => c.columnName.toLowerCase()))
|
||||
const hasTenantColumn = colNames.has('tenantid')
|
||||
const selectableColumns = cols.filter((c) => isAutoSelectedColumn(c.columnName, hasTenantColumn))
|
||||
setSelectedColumns(new Set(selectableColumns.map((c) => c.columnName)))
|
||||
setEditingGroups([])
|
||||
// Auto-check isTenant / isBranch based on column presence
|
||||
const colNames = new Set(cols.map((c) => c.columnName.toLowerCase()))
|
||||
formikRef.current?.setFieldValue('isTenant', colNames.has('tenantid'))
|
||||
formikRef.current?.setFieldValue('isTenant', hasTenantColumn)
|
||||
formikRef.current?.setFieldValue('isBranch', colNames.has('branchid'))
|
||||
// Auto-select first column as key field
|
||||
if (cols.length > 0) {
|
||||
|
|
@ -284,17 +301,23 @@ const Wizard = () => {
|
|||
return next
|
||||
})
|
||||
|
||||
const toggleAllColumns = (all: boolean) =>
|
||||
const toggleAllColumns = (all: boolean, isTenant = formikRef.current?.values.isTenant ?? false) =>
|
||||
setSelectedColumns(
|
||||
all
|
||||
? new Set(
|
||||
selectCommandColumns
|
||||
.filter((c) => !isAuditColumn(c.columnName))
|
||||
.filter((c) => isAutoSelectedColumn(c.columnName, isTenant))
|
||||
.map((c) => c.columnName),
|
||||
)
|
||||
: new Set(),
|
||||
)
|
||||
|
||||
const handleTenantChange = (isTenant: boolean) => {
|
||||
if (!isTenant) return
|
||||
setSelectedColumns((prev) => removeTenantColumn(prev))
|
||||
setEditingGroups((prev) => removeTenantGroupItems(prev))
|
||||
}
|
||||
|
||||
const getDataSourceList = async () => {
|
||||
setIsLoadingDataSource(true)
|
||||
const response = await getDataSources()
|
||||
|
|
@ -391,6 +414,7 @@ const Wizard = () => {
|
|||
languageTextMenuParentTr: w.languageTextMenuParentTr ?? '',
|
||||
permissionGroupName: w.permissionGroupName ?? '',
|
||||
menuParentCode: w.menuParentCode ?? '',
|
||||
menuParentIcon: w.menuParentIcon ?? '',
|
||||
menuIcon: w.menuIcon ?? '',
|
||||
dataSourceCode: w.dataSourceCode ?? '',
|
||||
dataSourceConnectionString: w.dataSourceConnectionString ?? '',
|
||||
|
|
@ -415,6 +439,7 @@ const Wizard = () => {
|
|||
widgets: w.widgets ?? [],
|
||||
workflow: w.workflow ?? {
|
||||
approvalUserFieldName: '',
|
||||
isFilterUserName: false,
|
||||
approvalDateFieldName: '',
|
||||
approvalStatusFieldName: '',
|
||||
approvalDescriptionFieldName: '',
|
||||
|
|
@ -453,6 +478,7 @@ const Wizard = () => {
|
|||
editorScript: it.editorScript ?? '',
|
||||
colSpan: it.colSpan ?? 1,
|
||||
isRequired: it.isRequired ?? false,
|
||||
includeInEditingForm: it.includeInEditingForm ?? true,
|
||||
turkishCaption: it.turkishCaption ?? it.dataField,
|
||||
englishCaption: it.englishCaption ?? it.dataField,
|
||||
captionName: it.captionName ?? `App.Listform.ListformField.${it.dataField}`,
|
||||
|
|
@ -477,6 +503,7 @@ const Wizard = () => {
|
|||
setWorkflow(
|
||||
w.workflow ?? {
|
||||
approvalUserFieldName: '',
|
||||
isFilterUserName: false,
|
||||
approvalDateFieldName: '',
|
||||
approvalStatusFieldName: '',
|
||||
approvalDescriptionFieldName: '',
|
||||
|
|
@ -516,24 +543,38 @@ const Wizard = () => {
|
|||
.trim()
|
||||
|
||||
const handleWizardNameChange = (name: string) => {
|
||||
const formik = formikRef.current
|
||||
const spacedLabel = toSpacedLabel(name)
|
||||
const previousSpacedLabel = toSpacedLabel(formik?.values.wizardName ?? '')
|
||||
const derived = deriveListFormCode(name)
|
||||
|
||||
formikRef.current?.setFieldValue('wizardName', name)
|
||||
formikRef.current?.setFieldValue('listFormCode', derived)
|
||||
formikRef.current?.setFieldValue('menuCode', derived)
|
||||
formikRef.current?.setFieldValue('languageTextMenuEn', spacedLabel)
|
||||
formikRef.current?.setFieldValue('languageTextMenuTr', spacedLabel)
|
||||
formikRef.current?.setFieldValue('languageTextTitleEn', spacedLabel)
|
||||
formikRef.current?.setFieldValue('languageTextTitleTr', spacedLabel)
|
||||
formikRef.current?.setFieldValue('languageTextDescEn', spacedLabel)
|
||||
formikRef.current?.setFieldValue('languageTextDescTr', spacedLabel)
|
||||
const setAutoText = (field: keyof Pick<
|
||||
ListFormWizardDto,
|
||||
| 'languageTextMenuEn'
|
||||
| 'languageTextMenuTr'
|
||||
| 'languageTextTitleEn'
|
||||
| 'languageTextTitleTr'
|
||||
| 'languageTextDescEn'
|
||||
| 'languageTextDescTr'
|
||||
>) => {
|
||||
const current = formik?.values[field]
|
||||
if (!current || current === previousSpacedLabel) {
|
||||
formik?.setFieldValue(field, spacedLabel)
|
||||
}
|
||||
}
|
||||
|
||||
formik?.setFieldValue('wizardName', name)
|
||||
formik?.setFieldValue('listFormCode', derived)
|
||||
formik?.setFieldValue('menuCode', derived)
|
||||
setAutoText('languageTextMenuEn')
|
||||
setAutoText('languageTextMenuTr')
|
||||
setAutoText('languageTextTitleEn')
|
||||
setAutoText('languageTextTitleTr')
|
||||
setAutoText('languageTextDescEn')
|
||||
setAutoText('languageTextDescTr')
|
||||
}
|
||||
|
||||
const handleMenuParentChange = (code: string) => {
|
||||
formikRef.current?.setFieldValue('menuParentCode', code)
|
||||
if (!code) return
|
||||
const rootCode = findRootCode(rawMenuItems, code)
|
||||
const applyPermissionGroupFromRoot = (rootCode: string) => {
|
||||
const rootItem = rawMenuItems.find((i) => i.code === rootCode)
|
||||
|
||||
// 1. Use group field if set
|
||||
|
|
@ -565,6 +606,32 @@ const Wizard = () => {
|
|||
formikRef.current?.setFieldValue('permissionGroupName', rootCode)
|
||||
}
|
||||
|
||||
const handleMenuParentChange = (code: string) => {
|
||||
formikRef.current?.setFieldValue('menuParentCode', code)
|
||||
const selectedMenu = rawMenuItems.find((item) => item.code === code)
|
||||
formikRef.current?.setFieldValue('menuParentIcon', selectedMenu?.icon ?? '')
|
||||
if (!code) return
|
||||
applyPermissionGroupFromRoot(findRootCode(rawMenuItems, code))
|
||||
}
|
||||
|
||||
const handleMenuCreated = async (menu: {
|
||||
code: string
|
||||
parentCode?: string
|
||||
menuTextEn: string
|
||||
menuTextTr: string
|
||||
icon?: string
|
||||
}) => {
|
||||
formikRef.current?.setFieldValue('menuParentCode', menu.code)
|
||||
formikRef.current?.setFieldValue('menuParentIcon', menu.icon ?? '')
|
||||
formikRef.current?.setFieldValue('languageTextMenuParentEn', menu.menuTextEn)
|
||||
formikRef.current?.setFieldValue('languageTextMenuParentTr', menu.menuTextTr)
|
||||
|
||||
const rootCode = menu.parentCode ? findRootCode(rawMenuItems, menu.parentCode) : menu.code
|
||||
applyPermissionGroupFromRoot(rootCode)
|
||||
|
||||
await getMenuList()
|
||||
}
|
||||
|
||||
const handleNext = async () => {
|
||||
if (!formikRef.current) return
|
||||
const errors = await formikRef.current.validateForm()
|
||||
|
|
@ -586,6 +653,19 @@ const Wizard = () => {
|
|||
const handleBack = () => setCurrentStep(0)
|
||||
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
||||
|
||||
const preventEnterSubmit = (event: KeyboardEvent<HTMLFormElement>) => {
|
||||
if (event.key !== 'Enter') return
|
||||
|
||||
const target = event.target as HTMLElement
|
||||
const tagName = target.tagName.toLowerCase()
|
||||
const isTextArea = tagName === 'textarea'
|
||||
const isExplicitSubmit = target.getAttribute('type') === 'submit'
|
||||
|
||||
if (!isTextArea && !isExplicitSubmit) {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
const handleNext2 = async () => {
|
||||
if (!formikRef.current) return
|
||||
const errors = await formikRef.current.validateForm()
|
||||
|
|
@ -627,6 +707,7 @@ const Wizard = () => {
|
|||
editorScript: item.editorScript ?? '',
|
||||
colSpan: item.colSpan,
|
||||
isRequired: item.isRequired,
|
||||
includeInEditingForm: item.includeInEditingForm,
|
||||
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12,
|
||||
turkishCaption: item.turkishCaption,
|
||||
englishCaption: item.englishCaption,
|
||||
|
|
@ -676,7 +757,10 @@ const Wizard = () => {
|
|||
/>
|
||||
|
||||
<div className="mb-6 mt-2">
|
||||
<Steps current={currentStep}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
className="flex flex-row flex-wrap !justify-start gap-y-2 lg:flex-nowrap lg:!justify-between"
|
||||
>
|
||||
<Steps.Item title={translate('::ListForms.Wizard.MenuInfo') || 'Menu Info'} />
|
||||
<Steps.Item
|
||||
title={translate('::ListForms.Wizard.ListFormSettings') || 'List Form Settings'}
|
||||
|
|
@ -701,40 +785,12 @@ const Wizard = () => {
|
|||
innerRef={formikRef}
|
||||
initialValues={{ ...initialValues }}
|
||||
validationSchema={listFormValidationSchema}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
|
||||
try {
|
||||
// 🔴 1. Kaydet (bekle)
|
||||
await postListFormWizard({ ...values })
|
||||
|
||||
// 🔴 2. Config güncelle (bekle)
|
||||
await getConfig(true)
|
||||
|
||||
// 🔴 3. Navigate
|
||||
navigate(
|
||||
ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode),
|
||||
{ replace: true },
|
||||
)
|
||||
|
||||
// 🔴 4. Toast (istersen navigate öncesi de olabilir)
|
||||
toast.push(
|
||||
<Notification type="success" duration={2000}>
|
||||
{translate('::ListForms.FormBilgileriKaydedildi')}
|
||||
</Notification>,
|
||||
{ placement: 'top-end' },
|
||||
)
|
||||
} catch (error: any) {
|
||||
toast.push(<Notification title={error.message} type="danger" />, {
|
||||
placement: 'top-end',
|
||||
})
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
onSubmit={(_, { setSubmitting }) => {
|
||||
setSubmitting(false)
|
||||
}}
|
||||
>
|
||||
{({ touched, errors, isSubmitting, values }) => (
|
||||
<Form>
|
||||
<Form onKeyDown={preventEnterSubmit}>
|
||||
<FormContainer size={currentStep >= 2 ? undefined : 'sm'}>
|
||||
{/* ─── Step 1: Basic Info ─────────────────────────────── */}
|
||||
{currentStep === 0 && (
|
||||
|
|
@ -748,7 +804,11 @@ const Wizard = () => {
|
|||
menuTree={menuTree}
|
||||
isLoadingMenu={isLoadingMenu}
|
||||
onMenuParentChange={handleMenuParentChange}
|
||||
onClearMenuParent={() => formikRef.current?.setFieldValue('menuParentCode', '')}
|
||||
onClearMenuParent={() => {
|
||||
formikRef.current?.setFieldValue('menuParentCode', '')
|
||||
formikRef.current?.setFieldValue('menuParentIcon', '')
|
||||
}}
|
||||
onMenuCreated={handleMenuCreated}
|
||||
onReloadMenu={getMenuList}
|
||||
permissionGroupList={permissionGroupList}
|
||||
isLoadingPermissionGroup={isLoadingPermissionGroup}
|
||||
|
|
@ -770,6 +830,7 @@ const Wizard = () => {
|
|||
onDataSourceNewChange={setIsDataSourceNew}
|
||||
dbObjects={dbObjects}
|
||||
isLoadingDbObjects={isLoadingDbObjects}
|
||||
onDbObjectsRefresh={loadDbObjects}
|
||||
selectCommandColumns={selectCommandColumns}
|
||||
isLoadingColumns={isLoadingColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
|
|
@ -779,7 +840,8 @@ const Wizard = () => {
|
|||
setSelectedColumns(new Set())
|
||||
}}
|
||||
onToggleColumn={toggleColumn}
|
||||
onToggleAllColumns={toggleAllColumns}
|
||||
onToggleAllColumns={(all) => toggleAllColumns(all, values.isTenant)}
|
||||
onTenantChange={handleTenantChange}
|
||||
translate={translate}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext2}
|
||||
|
|
|
|||
|
|
@ -414,6 +414,14 @@ export interface WizardStep1Props {
|
|||
isLoadingMenu: boolean
|
||||
onMenuParentChange: (code: string) => void
|
||||
onClearMenuParent: () => void
|
||||
onMenuCreated: (menu: {
|
||||
code: string
|
||||
parentCode?: string
|
||||
menuTextEn: string
|
||||
menuTextTr: string
|
||||
icon?: string
|
||||
shortName?: string
|
||||
}) => void | Promise<void>
|
||||
onReloadMenu: () => void
|
||||
permissionGroupList: SelectBoxOption[]
|
||||
isLoadingPermissionGroup: boolean
|
||||
|
|
@ -432,6 +440,7 @@ const WizardStep1 = ({
|
|||
isLoadingMenu,
|
||||
onMenuParentChange,
|
||||
onClearMenuParent,
|
||||
onMenuCreated,
|
||||
onReloadMenu,
|
||||
permissionGroupList,
|
||||
isLoadingPermissionGroup,
|
||||
|
|
@ -544,7 +553,7 @@ const WizardStep1 = ({
|
|||
initialParentCode={menuDialogParentCode}
|
||||
initialOrder={menuDialogInitialOrder}
|
||||
rawItems={rawMenuItems}
|
||||
onSaved={onReloadMenu}
|
||||
onSaved={onMenuCreated}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import { Button, Checkbox, FormItem, Input, Select } from '@/components/ui'
|
|||
import { SelectCommandTypeEnum } from '@/proxy/form/models'
|
||||
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
||||
import { SelectBoxOption } from '@/types/shared'
|
||||
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||
import { Field, FieldProps, FormikErrors, FormikTouched, useFormikContext } from 'formik'
|
||||
import { useState } from 'react'
|
||||
import CreatableSelect from 'react-select/creatable'
|
||||
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
|
||||
import { FaArrowLeft, FaArrowRight, FaPlus } from 'react-icons/fa'
|
||||
import { dbSourceTypeOptions, listFormDefaultLayoutOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from '../edit/options'
|
||||
import { ListFormWizardDto } from '@/proxy/admin/wizard/models'
|
||||
import SqlTableDesignerDialog from '@/views/developerKit/SqlTableDesignerDialog'
|
||||
|
||||
// ─── Props ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -23,6 +25,7 @@ export interface WizardStep2Props {
|
|||
// DB Objects
|
||||
dbObjects: SqlObjectExplorerDto | null
|
||||
isLoadingDbObjects: boolean
|
||||
onDbObjectsRefresh: (dsCode: string) => void | Promise<void>
|
||||
// Columns
|
||||
selectCommandColumns: DatabaseColumnDto[]
|
||||
isLoadingColumns: boolean
|
||||
|
|
@ -31,6 +34,7 @@ export interface WizardStep2Props {
|
|||
onClearColumns: () => void
|
||||
onToggleColumn: (col: string) => void
|
||||
onToggleAllColumns: (all: boolean) => void
|
||||
onTenantChange: (isTenant: boolean) => void
|
||||
// Navigation
|
||||
translate: (key: string) => string
|
||||
onBack: () => void
|
||||
|
|
@ -50,6 +54,7 @@ const WizardStep2 = ({
|
|||
onDataSourceNewChange,
|
||||
dbObjects,
|
||||
isLoadingDbObjects,
|
||||
onDbObjectsRefresh,
|
||||
selectCommandColumns,
|
||||
isLoadingColumns,
|
||||
selectedColumns,
|
||||
|
|
@ -57,10 +62,23 @@ const WizardStep2 = ({
|
|||
onClearColumns,
|
||||
onToggleColumn,
|
||||
onToggleAllColumns,
|
||||
onTenantChange,
|
||||
translate,
|
||||
onBack,
|
||||
onNext,
|
||||
}: WizardStep2Props) => {
|
||||
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
|
||||
const formik = useFormikContext<ListFormWizardDto>()
|
||||
|
||||
const handleTableDeployed = async (table: { schemaName: string; tableName: string }) => {
|
||||
await onDbObjectsRefresh(values.dataSourceCode)
|
||||
formik.setFieldValue('selectCommand', table.tableName)
|
||||
formik.setFieldValue('selectCommandType', SelectCommandTypeEnum.Table)
|
||||
formik.setFieldValue('keyFieldName', '')
|
||||
formik.setFieldTouched('keyFieldName', false)
|
||||
onLoadColumns(values.dataSourceCode, table.schemaName || 'dbo', table.tableName)
|
||||
}
|
||||
|
||||
const step2Missing = [
|
||||
!values.listFormCode && translate('::App.Listform.ListformField.ListFormCode'),
|
||||
!values.dataSourceCode && translate('::ListForms.Wizard.Step4.DataSource'),
|
||||
|
|
@ -217,7 +235,9 @@ const WizardStep2 = ({
|
|||
]
|
||||
: []
|
||||
return (
|
||||
<Select
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<Select
|
||||
field={field}
|
||||
form={form}
|
||||
isClearable
|
||||
|
|
@ -256,7 +276,18 @@ const WizardStep2 = ({
|
|||
form.setFieldTouched('keyFieldName', false)
|
||||
onClearColumns()
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="solid"
|
||||
icon={<FaPlus />}
|
||||
disabled={!values.dataSourceCode}
|
||||
onClick={() => setShowTableDesignerDialog(true)}
|
||||
>
|
||||
New Table
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Field>
|
||||
|
|
@ -328,7 +359,18 @@ const WizardStep2 = ({
|
|||
invalid={!!(errors.isTenant && touched.isTenant)}
|
||||
errorMessage={errors.isTenant}
|
||||
>
|
||||
<Field type="checkbox" autoComplete="off" name="isTenant" component={Checkbox} />
|
||||
<Field name="isTenant">
|
||||
{({ field, form }: FieldProps<boolean>) => (
|
||||
<Checkbox
|
||||
name={field.name}
|
||||
checked={Boolean(field.value)}
|
||||
onChange={(checked) => {
|
||||
form.setFieldValue(field.name, checked)
|
||||
onTenantChange(checked)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
|
|
@ -700,6 +742,7 @@ const WizardStep2 = ({
|
|||
<div className="flex items-center gap-2 ml-3">
|
||||
<Button
|
||||
variant="solid"
|
||||
type="button"
|
||||
onClick={() => onToggleAllColumns(true)}
|
||||
className="text-xs px-2 py-0.5 rounded bg-indigo-500 text-white hover:bg-indigo-600"
|
||||
>
|
||||
|
|
@ -707,6 +750,7 @@ const WizardStep2 = ({
|
|||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
type="button"
|
||||
onClick={() => onToggleAllColumns(false)}
|
||||
className="text-xs px-2 py-0.5 rounded border border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400"
|
||||
>
|
||||
|
|
@ -785,6 +829,14 @@ const WizardStep2 = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SqlTableDesignerDialog
|
||||
isOpen={showTableDesignerDialog}
|
||||
onClose={() => setShowTableDesignerDialog(false)}
|
||||
dataSource={values.dataSourceCode}
|
||||
initialTableData={null}
|
||||
onDeployed={handleTableDeployed}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue