Compare commits

..

No commits in common. "main" and "1.1.03" have entirely different histories.
main ... 1.1.03

146 changed files with 3486 additions and 7745 deletions

View file

@ -17,9 +17,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>
</Project>

View file

@ -12,9 +12,9 @@
<PrivateAssets>All</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>

View file

@ -31,8 +31,6 @@ 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);

View file

@ -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" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>
<ItemGroup>

View file

@ -12,9 +12,9 @@
<PrivateAssets>All</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>

View file

@ -65,7 +65,6 @@ public class AmazonSesEmailSender : EmailSenderBase, ISozsoftEmailSender, ITrans
await BackgroundJobManager.EnqueueAsync(
new ErpBackgroundEmailSendingJobArgs
{
TenantId = CurrentTenant.Id,
To = to,
Sender = sender,
Params = @params,

View file

@ -1,6 +1,5 @@
using Volo.Abp.BackgroundJobs;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Sender.Mail;
@ -8,28 +7,22 @@ public class ErpBackgroundEmailSendingJob :
AsyncBackgroundJob<ErpBackgroundEmailSendingJobArgs>, ITransientDependency
{
protected ISozsoftEmailSender EmailSender { get; }
protected ICurrentTenant CurrentTenant { get; }
public ErpBackgroundEmailSendingJob(
ISozsoftEmailSender emailSender,
ICurrentTenant currentTenant)
public ErpBackgroundEmailSendingJob(ISozsoftEmailSender emailSender)
{
EmailSender = emailSender;
CurrentTenant = currentTenant;
}
public override async Task ExecuteAsync(ErpBackgroundEmailSendingJobArgs args)
{
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);
}
//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);
}
}

View file

@ -1,12 +1,8 @@
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Sender.Mail;
namespace Sozsoft.Sender.Mail;
[Serializable]
public class ErpBackgroundEmailSendingJobArgs : IMultiTenant
public class ErpBackgroundEmailSendingJobArgs
{
public Guid? TenantId { get; set; }
public string[] To { get; set; }
public KeyValuePair<string, string>? Sender { get; set; }
@ -18,4 +14,6 @@ public class ErpBackgroundEmailSendingJobArgs : IMultiTenant
public Dictionary<string, string>? Attachments { get; set; }
public string? TextContent { get; set; }
}

View file

@ -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" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>
</Project>

View file

@ -5,9 +5,10 @@ using System.Threading.Tasks;
using Sozsoft.Languages;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.SettingManagement;
using Volo.Abp.Settings;
using SettingDefinition = Sozsoft.Settings.Entities.SettingDefinition;
namespace Sozsoft.Settings;
@ -19,20 +20,17 @@ 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,
ICurrentTenant currentTenant)
ErpSettingDefinitionManager ErpSettingDefinitionManager)
{
this.languageKeyIntegrationService = languageKeyIntegrationService;
this.settingDefinitionManager = settingDefinitionManager;
this.settingManager = settingManager;
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
this.currentTenant = currentTenant;
}
public virtual async Task<List<MainGroupedSettingDto>> GetListAsync()
@ -97,18 +95,15 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
{
if (setting.Providers.IsNullOrEmpty())
{
if (currentTenant.Id.HasValue)
{
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
}
else
{
await settingManager.SetGlobalAsync(setting.Name, value);
}
await settingManager.SetForCurrentUserAsync(setting.Name, value);
}
else
{
if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName) && currentTenant.Id.HasValue)
if (setting.Providers.Any(p => p == UserSettingValueProvider.ProviderName))
{
await settingManager.SetForCurrentUserAsync(setting.Name, value);
}
else if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName))
{
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
}
@ -116,10 +111,6 @@ 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);
}
}
}

View file

@ -28,38 +28,22 @@ 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 = 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)
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)
.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);
}
}

View file

@ -1,6 +1,5 @@
using Volo.Abp.Modularity;
using Volo.Abp.Modularity;
using Volo.Abp.SettingManagement;
using Volo.Abp.Settings;
namespace Sozsoft.Settings;
@ -12,10 +11,7 @@ public class SettingsDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpSettingOptions>(options =>
{
options.DefinitionProviders.Remove<SettingsDefinitionProvider>();
options.DefinitionProviders.Add<SettingsDefinitionProvider>();
});
}
}

View file

@ -12,9 +12,9 @@
<PrivateAssets>All</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>

View file

@ -12,9 +12,9 @@
<PrivateAssets>All</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="MimeKit" Version="4.16.0" />
<PackageReference Include="Scriban" Version="7.2.3" />
<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" />
</ItemGroup>

View file

@ -1,9 +0,0 @@
using Volo.Abp.Application.Dtos;
namespace Sozsoft.Platform.AuditLogs;
public class AuditLogListRequestDto : PagedAndSortedResultRequestDto
{
public string ListFormCode { get; set; }
public string EntityId { get; set; }
}

View file

@ -1,11 +0,0 @@
using System;
using Volo.Abp.Content;
namespace Sozsoft.Platform.Identity.Dto;
public class UserAvatarUpdateInput
{
public Guid UserId { get; set; }
public IRemoteStreamContent Avatar { get; set; }
}

View file

@ -5,7 +5,6 @@ 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; }

View file

@ -39,7 +39,6 @@ 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; }

View file

@ -12,7 +12,6 @@ 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; }

View file

@ -1,5 +1,3 @@
using System.Collections.Generic;
namespace Sozsoft.Platform.ListForms;
public class WorkflowRunResultDto
@ -10,6 +8,5 @@ public class WorkflowRunResultDto
public string CurrentNodeKind { get; set; }
public bool WaitingApproval { get; set; }
public bool Completed { get; set; }
public List<string> ToastMessages { get; set; } = [];
}

View file

@ -7,7 +7,6 @@ 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; }

View file

@ -1,43 +1,30 @@
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, AuditLogListRequestDto>
: ICrudAppService<AuditLogDto, Guid>
{
}
[Authorize(AppCodes.IdentityManagement.AuditLogs)]
public class AuditLogAppService : CrudAppService<
AuditLog,
AuditLogDto,
Guid,
AuditLogListRequestDto>, IAuditLogAppService
public class AuditLogAppService
: CrudAppService<AuditLog, AuditLogDto, Guid>
, IAuditLogAppService
{
private readonly IRepository<ListForm, Guid> _listFormRepository;
public AuditLogAppService(
IAuditLogRepository auditLogRepository,
IRepository<ListForm, Guid> listFormRepository
) : base(auditLogRepository)
public AuditLogAppService(IAuditLogRepository auditLogRepository) : base(auditLogRepository)
{
_listFormRepository = listFormRepository;
}
public override async Task<AuditLogDto> GetAsync(Guid id)
@ -48,26 +35,23 @@ public class AuditLogAppService : CrudAppService<
}
[UnitOfWork]
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogListRequestDto input)
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(PagedAndSortedResultRequestDto 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 query = await CreateFilteredQueryAsync(input);
var totalCount = await AsyncExecuter.CountAsync(query);
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
var auditLogsWithDetails = await AsyncExecuter.ToListAsync(query);
// 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
);
// Mapping tek seferde yap
var entityDtos = ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogsWithDetails);
@ -85,102 +69,6 @@ public class AuditLogAppService : CrudAppService<
);
}
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)

View file

@ -76,7 +76,8 @@ 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 userDetailUrl = await GetUserDetailUrlAsync(user.Id);
var url = await appUrlProvider.GetUrlAsync("MVC", PlatformConsts.Urls.UserDetail);
var userDetailUrl = $"{url}/{user.Id}";
var content = $@"My name is: {user.GetFullName()}.
Email Address: {user.Email}
@ -110,16 +111,17 @@ User Detail: {userDetailUrl}";
}
[Captcha]
public async Task<bool> SendExtendLoginRequestAsync(SendExtendLoginRequestInputDto input)
public async Task SendExtendLoginRequestAsync(SendExtendLoginRequestInputDto input)
{
var user = await UserManager.FindByEmailAsync(input.EmailAddress);
if (user == null)
{
return false;
return;
}
var userDetailUrl = await appUrlProvider.GetUrlAsync(PlatformConsts.React, PlatformConsts.Urls.UserDetail);
var content = $@"My name is: {user.GetFullName()}.
User Detail: {await GetUserDetailUrlAsync(user.Id)}";
User Detail: {string.Format(userDetailUrl, user.Id)}";
var recipient = await settingProvider.GetOrNullAsync(PlatformConsts.AbpSettings.SiteManagement.General.TimedLoginEmails);
if (!recipient.IsNullOrWhiteSpace())
@ -130,23 +132,17 @@ User Detail: {await GetUserDetailUrlAsync(user.Id)}";
null,
content,
subject: PlatformConsts.AppName + " : Extend Login Request");
return true;
}
return false;
return;
}
[Captcha]
public async Task<bool> SendAccountConfirmationCodeAsync(SendAccountConfirmationCodeInputDto input)
public async Task SendAccountConfirmationCodeAsync(SendAccountConfirmationCodeInputDto input)
{
var user = await UserManager.FindByEmailAsync(input.EmailAddress);
if (user == null)
{
return false;
}
return await SendConfirmationCodeAsync(user);
if (user != null)
await SendConfirmationCodeAsync(user);
}
public async Task<bool> VerifyAccountConfirmationCodeAsync(VerifyAccountConfirmationCodeInputDto input)
@ -207,15 +203,6 @@ 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}";
}
}

View file

@ -3,12 +3,10 @@ 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;
@ -34,7 +32,6 @@ 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,
@ -48,7 +45,6 @@ public class PlatformIdentityAppService : ApplicationService
IRepository<WorkHour, Guid> workHourRepository,
IRepository<Department, Guid> departmentRepository,
IRepository<JobPosition, Guid> jobPositionRepository,
BlobManager blobCdnManager,
IGuidGenerator guidGenerator
)
{
@ -59,7 +55,6 @@ 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;
@ -278,22 +273,6 @@ 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();

View file

@ -18,7 +18,6 @@ using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
namespace Sozsoft.Platform.Intranet;
@ -28,7 +27,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;
@ -50,7 +49,6 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
ICurrentTenant currentTenant,
BlobManager blobContainer,
IConfiguration configuration,
ILookupNormalizer lookupNormalizer,
IRepository<Event, Guid> eventRepository,
IIdentityUserAppService identityUserAppService,
@ -73,7 +71,6 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
_currentTenant = currentTenant;
_blobContainer = blobContainer;
_configuration = configuration;
_lookupNormalizer = lookupNormalizer;
_eventRepository = eventRepository;
_identityUserAppService = identityUserAppService;
_identityUserRepository = identityUserRepository;
@ -117,12 +114,12 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
}
private UserInfoViewModel MapUserInfoViewModel(
Volo.Abp.Identity.IdentityUser user,
IdentityUser user,
IReadOnlyDictionary<Guid, string> departmentDict,
IReadOnlyDictionary<Guid, JobPosition> jobPositionDict)
{
return ObjectMapper
.Map<Volo.Abp.Identity.IdentityUser, UserInfoViewModel>(user)
.Map<IdentityUser, UserInfoViewModel>(user)
.MapDepartmentAndJobPositionAssignments(departmentDict, jobPositionDict);
}
@ -130,8 +127,9 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
IReadOnlyDictionary<Guid, string> departmentDict,
IReadOnlyDictionary<Guid, JobPosition> jobPositionDict)
{
var normalizedAdmin = _lookupNormalizer.NormalizeName(PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue);
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(normalizedAdmin);
var normalizedAdmin = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(normalizedAdmin)
?? await _identityUserRepository.FindByNormalizedEmailAsync(normalizedAdmin);
if (user == null && CurrentUser.Id.HasValue)
{

View file

@ -3,13 +3,11 @@ 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 IdentityUser = Volo.Abp.Identity.IdentityUser;
using Volo.Abp.Data;
namespace Sozsoft.Platform.ListForms.DynamicApi;
@ -20,125 +18,79 @@ 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,
IdentityUserManager userManager,
IOptions<IdentityOptions> identityOptions)
IIdentityRoleAppService identityRoleAppService)
{
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)
{
await identityOptions.SetAsync();
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)
var user = new IdentityUserCreateDto
{
UserName = input.Data.Email,
Name = input.Data.Name,
Surname = input.Data.Surname
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);
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);
await identityUserAppService.CreateAsync(user);
}
[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 user = await userManager.GetByIdAsync(id);
if (user == null)
var entity = await identityUserAppService.GetAsync(id) ?? throw new EntityNotFoundException(L["RecordNotFound"]);
var user = new IdentityUserUpdateDto
{
throw new EntityNotFoundException(L["RecordNotFound"]);
}
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();
}
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())
{
if (await userManager.HasPasswordAsync(user))
{
(await userManager.RemovePasswordAsync(user)).CheckErrors();
}
(await userManager.AddPasswordAsync(user, input.Data.Password)).CheckErrors();
user.Password = input.Data.Password;
}
if (input.Data.WorkHour != null)
{
user.SetWorkHour(input.Data.WorkHour);
user.SetProperty(PlatformConsts.AbpIdentity.User.WorkHour, input.Data.WorkHour);
}
if (input.Data.DepartmentId != null)
{
user.SetDepartmentId(ParseGuid(input.Data.DepartmentId));
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, input.Data.DepartmentId);
}
if (input.Data.JobPositionId != null)
{
user.SetJobPositionId(ParseGuid(input.Data.JobPositionId));
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, input.Data.JobPositionId);
}
(await userManager.UpdateAsync(user)).CheckErrors();
await identityUserAppService.UpdateAsync(id, user);
}
//RoleAppService
@ -245,5 +197,6 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
var id = Guid.Parse(input.Keys[0]!.ToString()!);
await tenantRepository.DeleteAsync(id);
}
}

View file

@ -12,6 +12,8 @@ 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;
@ -58,21 +60,10 @@ public class ListFormWizardAppService(
public async Task Create(ListFormWizardDto input)
{
var wizardName = input.WizardName.Trim();
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";
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
var nameLangKey = WizardConsts.WizardKey(wizardName);
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
var code = WizardConsts.WizardKey(wizardName);
// Eklenen kayıtları takip et (silme işleminde kullanılır)
var inserted = new WizardInsertedRecordsDto();
@ -87,15 +78,16 @@ public class ListFormWizardAppService(
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
await EnsureLangKey(groupName, inserted);
else
await CreateLangKey(groupName, groupName, groupName, inserted);
await CreateLangKey(groupName, groupName, groupName, inserted);
inserted.PermissionGroupNames.Add(groupName);
}
// Permission'ları tek seferde kontrol et ve oluştur
var existingPerms = await repoPerm.GetListAsync(a => a.GroupName == groupName);
var queryable = await repoPerm.GetQueryableAsync();
var existingPerms = await AsyncExecuter.ToListAsync(
queryable.Where(a => a.GroupName == groupName)
);
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
if (permRead == null)
{
@ -103,45 +95,45 @@ public class ListFormWizardAppService(
inserted.PermissionNames.Add(permRead.Name);
}
var permCreate = existingPerms.FirstOrDefault(a => a.Name == permCreateName);
var permCreate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermCreate(wizardName));
if (permCreate == null)
{
permCreate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
permCreate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermCreate(wizardName), permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permCreate.Name);
}
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == permUpdateName);
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermUpdate(wizardName));
if (permUpdate == null)
{
permUpdate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
permUpdate = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermUpdate(wizardName), permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permUpdate.Name);
}
var permDelete = existingPerms.FirstOrDefault(a => a.Name == permDeleteName);
var permDelete = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermDelete(wizardName));
if (permDelete == null)
{
permDelete = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
permDelete = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermDelete(wizardName), permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permDelete.Name);
}
var permExport = existingPerms.FirstOrDefault(a => a.Name == permExportName);
var permExport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermExport(wizardName));
if (permExport == null)
{
permExport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
permExport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermExport(wizardName), permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permExport.Name);
}
var permImport = existingPerms.FirstOrDefault(a => a.Name == permImportName);
var permImport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermImport(wizardName));
if (permImport == null)
{
permImport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
permImport = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermImport(wizardName), permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permImport.Name);
}
var permNote = existingPerms.FirstOrDefault(a => a.Name == permNoteName);
var permNote = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermNote(wizardName));
if (permNote == null)
{
permNote = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
permNote = await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, WizardConsts.PermNote(wizardName), permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
inserted.PermissionNames.Add(permNote.Name);
}
@ -169,18 +161,12 @@ public class ListFormWizardAppService(
if (menuParent == null)
{
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
? input.MenuParentIcon
: !string.IsNullOrWhiteSpace(input.MenuIcon)
? input.MenuIcon
: WizardConsts.MenuIcon;
await CreateLangKey(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
await CreateLangKey(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
menuParent = await repoMenu.InsertAsync(new Menu
{
Code = input.MenuParentCode,
DisplayName = input.MenuParentCode,
DisplayName = WizardConsts.WizardKeyParent(wizardName),
IsDisabled = false,
Icon = menuParentIcon,
Order = maxRootOrder + 1,
}, autoSave: false);
inserted.MenuCodes.Add(input.MenuParentCode);
@ -232,7 +218,7 @@ public class ListFormWizardAppService(
ColSpan = g.ColCount,
ItemType = "group",
Items = g.Items
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
.Where(i => i.DataField != input.KeyFieldName)
.Select((it, ii) => new EditingFormItemDto
{
Order = ii + 1,
@ -245,7 +231,6 @@ public class ListFormWizardAppService(
})
.ToArray()
})
.Where(g => g.Items.Length > 0)
.ToList();
//ListForm - varsa sil, yeniden ekle
@ -272,15 +257,14 @@ public class ListFormWizardAppService(
var isCreated = tableColumns.Contains("CreatorId");
input.Workflow ??= new WorkflowDto();
input.Workflow.Criteria = input.WorkflowCriteria;
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
await repoListForm.InsertAsync(new ListForm
var listForm = await repoListForm.InsertAsync(new ListForm
{
ListFormType = ListFormTypeEnum.List,
PageSize = 10,
ExportJson = WizardConsts.DefaultExportJson,
IsSubForm = false,
ShowNote = input.SubForms.Count > 0 || input.WorkflowCriteria.Count > 0,
ShowNote = input.SubForms.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,
@ -296,12 +280,12 @@ public class ListFormWizardAppService(
KeyFieldName = input.KeyFieldName,
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
SortMode = PlatformConsts.GridOptions.SortModeSingle,
SortMode = GridOptions.SortModeSingle,
FilterRowJson = WizardConsts.DefaultFilterRowJson,
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? PlatformConsts.GridOptions.SelectionModeSingle : PlatformConsts.GridOptions.SelectionModeNone),
SelectionJson = WizardConsts.DefaultSelectionSingleJson,
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
PermissionJson = WizardConsts.DefaultPermissionJson(code),
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
@ -411,65 +395,13 @@ 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.
@ -745,16 +677,4 @@ 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;
}
}

View file

@ -7,12 +7,10 @@ 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;
@ -31,7 +29,6 @@ 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;
@ -39,22 +36,18 @@ 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,
IStringLocalizer<PlatformResource> localizer)
ISettingProvider settingProvider)
{
this.criteriaRepository = criteriaRepository;
this.noteRepository = noteRepository;
this.listFormManager = listFormManager;
this.authManager = authManager;
this.listFormSelectAppService = listFormSelectAppService;
@ -62,7 +55,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
this.identityUserManager = identityUserManager;
this.erpEmailSender = erpEmailSender;
this.settingProvider = settingProvider;
this.localizer = localizer;
}
[HttpGet("criteria")]
@ -196,7 +188,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
criteria.ListFormCode = code;
criteria.Kind = NormalizeRequired(input.Kind, "Compare");
criteria.Title = await NormalizeUniqueTitleAsync(code, criteria.Id, input.Kind, input.Title);
criteria.Title = NormalizeRequired(input.Title, criteria.Kind);
criteria.CompareColumn = NormalizeRequired(input.CompareColumn, "Price");
criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">");
criteria.CompareValue = input.CompareValue;
@ -308,14 +300,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
context.WorkflowNoteRows.Add(("Started By: ", ResolveCurrentUserDisplayName()));
var result = await RunUntilWaitAsync(context, start);
await InsertWorkflowNoteAsync(
context,
$"Workflow Started: {start.Title}",
BuildWorkflowNoteContent(context.WorkflowNoteRows));
return result;
return await RunUntilWaitAsync(context, start);
}
[HttpPost("decision")]
@ -380,15 +365,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
}
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
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;
return await RunUntilWaitAsync(context, next);
}
private async Task<WorkflowRunResultDto> RunForEachKeyAsync(
@ -410,10 +387,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
CurrentNodeTitle = last?.CurrentNodeTitle,
CurrentNodeKind = last?.CurrentNodeKind,
WaitingApproval = results.Any(x => x.WaitingApproval),
Completed = results.All(x => x.Completed),
ToastMessages = results
.SelectMany(result => result.ToastMessages ?? [])
.ToList()
Completed = results.All(x => x.Completed)
};
}
@ -445,7 +419,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
.ToList();
var row = await GetRowAsync(code, listForm.KeyFieldName, keys[0]);
return new WorkflowRunContext(code, keys, workflow, criteria, row);
return new WorkflowRunContext(code, listForm.KeyFieldName, keys, workflow, criteria, row);
}
private async Task<IDictionary<string, object>> GetRowAsync(string listFormCode, string keyFieldName, object key)
@ -537,8 +511,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
await UpdateRowAsync(context, update);
MergeRowValues(context.Row, update);
AddWorkflowNodeRows(context, node);
AddWorkflowToastMessage(context, node);
if (node.Kind == "Inform")
{
@ -561,7 +533,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
recipientEmail,
sender,
new { },
BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context)),
BuildInformEmailBody(context, node),
$"Workflow Bilgilendirme: {node.Title}",
null,
true);
@ -570,7 +542,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
{
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
}
}
private async Task<string> ResolveApproverEmailAsync(string approver)
@ -594,257 +565,22 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
return user.Email;
}
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)
private static string BuildInformEmailBody(WorkflowRunContext context, ListFormWorkflow node)
{
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 $"""
<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>
<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>
""";
}
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>(
@ -1026,8 +762,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
CurrentNodeTitle = node?.Title,
CurrentNodeKind = node?.Kind,
WaitingApproval = waitingApproval,
Completed = completed,
ToastMessages = context.ToastMessages.ToList()
Completed = completed
};
}
@ -1171,36 +906,6 @@ 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 ?? []);
@ -1242,14 +947,12 @@ 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; } = [];
}
}

View file

@ -30,15 +30,13 @@ 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,
LanguageTextAppService languageTextAppService
IPermissionDefinitionRecordRepository permissionRepository
) : base(menuRepository)
{
_menuRepository = menuRepository;
@ -46,7 +44,6 @@ public class MenuAppService : CrudAppService<
_repositoryText = languageTextRepository;
_tenantRepository = tenantRepository;
_permissionRepository = permissionRepository;
_languageTextAppService = languageTextAppService;
CreatePolicyName = $"{AppCodes.Menus.Menu}.Create";
UpdatePolicyName = $"{AppCodes.Menus.Menu}.Update";
@ -278,7 +275,7 @@ public class MenuAppService : CrudAppService<
if (existingEnText != null)
{
existingEnText.Value = input.MenuTextEn;
await _repositoryText.UpdateAsync(existingEnText, autoSave: true);
await _repositoryText.UpdateAsync(existingEnText);
}
else
{
@ -288,7 +285,7 @@ public class MenuAppService : CrudAppService<
CultureName = "en",
Value = input.MenuTextEn,
ResourceName = PlatformConsts.AppName
}, autoSave: true);
});
}
// Türkçe text oluşturuluyor veya güncelleniyor.
@ -300,7 +297,7 @@ public class MenuAppService : CrudAppService<
if (existingTrText != null)
{
existingTrText.Value = input.MenuTextTr;
await _repositoryText.UpdateAsync(existingTrText, autoSave: true);
await _repositoryText.UpdateAsync(existingTrText);
}
else
{
@ -310,12 +307,9 @@ 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);
}
}

View file

@ -195,13 +195,20 @@ public class PublicAppService : PlatformAppService
public async Task<HomeDto> GetHomeAsync()
{
var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home));
var entity = await _homeRepository.FirstOrDefaultAsync();
if (entity == null)
{
entity = await _homeRepository.InsertAsync(CreateDefaultHomeEntity(), autoSave: true);
}
return ObjectMapper.Map<Home, HomeDto>(entity);
}
public async Task SaveHomePageAsync(SaveHomePageInput input)
{
var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home));
var entity = await _homeRepository.FirstOrDefaultAsync();
var isNewEntity = entity == null;
entity ??= CreateDefaultHomeEntity();
entity.HeroBackgroundImageKey = input.HeroBackgroundImageKey;
entity.HeroPrimaryCtaKey = input.HeroPrimaryCtaKey;
@ -245,7 +252,14 @@ public class PublicAppService : PlatformAppService
StyleClass = solution.StyleClass,
}).ToList());
await _homeRepository.UpdateAsync(entity, autoSave: false);
if (isNewEntity)
{
await _homeRepository.InsertAsync(entity, autoSave: false);
}
else
{
await _homeRepository.UpdateAsync(entity, autoSave: false);
}
await UpsertLanguageTextAsync(input.CultureName, input.HeroBackgroundImageKey, input.HeroBackgroundImageValue);
await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaKey, input.HeroPrimaryCtaValue);
@ -594,6 +608,83 @@ 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())

View file

@ -8,101 +8,13 @@
}
],
"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": "en",
"defaultValue": "tr",
"isVisibleToClients": false,
"providers": "G|D",
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
"mainGroupKey": "App.SiteManagement",
@ -128,7 +40,7 @@
"tr": "Türkçe",
"zh-Hans": "繁體中文"
},
"order": 4
"order": 1
},
{
"code": "Abp.Timing.TimeZone",
@ -136,7 +48,7 @@
"descriptionKey": "Abp.Timing.TimeZone.Description",
"defaultValue": "Turkey Standard Time",
"isVisibleToClients": false,
"providers": "G|D",
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
"mainGroupKey": "App.SiteManagement",
@ -284,6 +196,94 @@
"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,22 +574,6 @@
"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",
@ -604,7 +588,7 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 29
"order": 28
},
{
"code": "Abp.Mailing.AWS.Region",
@ -620,13 +604,29 @@
"requiredPermissionName": "Abp.Mailing.AWS",
"dataType": "Text",
"selectOptions": {},
"order": 30
"order": 29
},
{
"code": "Abp.Mailing.AWS.AccessKey",
"nameKey": "Abp.Mailing.AWS.AccessKey",
"descriptionKey": "Abp.Mailing.AWS.AccessKey.Description",
"defaultValue": "SibFBAMiSApvz+NChYmlgZmx25JNbximemIDOFps",
"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",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -638,27 +638,11 @@
"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": "False",
"defaultValue": "True",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -690,8 +674,8 @@
"code": "Abp.Account.TwoFactor.Enabled",
"nameKey": "Abp.Account.TwoFactor.Enabled",
"descriptionKey": "Abp.Account.TwoFactor.Enabled.Description",
"defaultValue": "False",
"isVisibleToClients": true,
"defaultValue": "True",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
@ -738,7 +722,7 @@
"code": "Abp.Account.Captcha.SiteKey",
"nameKey": "Abp.Account.Captcha.SiteKey",
"descriptionKey": "Abp.Account.Captcha.SiteKey.Description",
"defaultValue": "0x4AAAAAABdEjmiXxcl0j7jp",
"defaultValue": "0x4AAAAAAAGadwQME-GSYuJU",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -754,7 +738,7 @@
"code": "Abp.Account.Captcha.SecretKey",
"nameKey": "Abp.Account.Captcha.SecretKey",
"descriptionKey": "Abp.Account.Captcha.SecretKey.Description",
"defaultValue": "0x4AAAAAABdEjhw1A8sJZUvQX8-CgqvB3mE",
"defaultValue": "0x4AAAAAAAGad_f_WP47IcNBs9FTu5DhNX8",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -770,8 +754,8 @@
"code": "Abp.Identity.Profile.General.RequireVerifiedAccount",
"nameKey": "Abp.Identity.Profile.General.RequireVerifiedAccount",
"descriptionKey": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description",
"defaultValue": "False",
"isVisibleToClients": true,
"defaultValue": "True",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
"isEncrypted": false,
@ -798,104 +782,6 @@
"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",
@ -910,13 +796,13 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 70
"order": 52
},
{
"code": "Abp.Identity.Password.PasswordChangePeriodDays",
"nameKey": "Abp.Identity.Password.PasswordChangePeriodDays",
"descriptionKey": "Abp.Identity.Password.PasswordChangePeriodDays.Description",
"defaultValue": "180",
"defaultValue": "0",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -926,7 +812,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 71
"order": 53
},
{
"code": "Abp.Identity.Password.RequiredLength",
@ -942,7 +828,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 72
"order": 54
},
{
"code": "Abp.Identity.Password.RequiredUniqueChars",
@ -958,7 +844,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Number",
"selectOptions": {},
"order": 73
"order": 55
},
{
"code": "Abp.Identity.Password.RequireNonAlphanumeric",
@ -974,7 +860,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 74
"order": 56
},
{
"code": "Abp.Identity.Password.RequireLowercase",
@ -990,7 +876,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 75
"order": 57
},
{
"code": "Abp.Identity.Password.RequireUppercase",
@ -1006,7 +892,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 76
"order": 58
},
{
"code": "Abp.Identity.Password.RequireDigit",
@ -1022,7 +908,7 @@
"requiredPermissionName": "Abp.Identity.Password",
"dataType": "Bool",
"selectOptions": {},
"order": 77
"order": 59
},
{
"code": "Abp.Identity.Lockout.AllowedForNewUsers",
@ -1038,7 +924,7 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Bool",
"selectOptions": {},
"order": 78
"order": 60
},
{
"code": "Abp.Identity.Lockout.LockoutDuration",
@ -1054,7 +940,7 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Number",
"selectOptions": {},
"order": 79
"order": 61
},
{
"code": "Abp.Identity.Lockout.MaxFailedAccessAttempts",
@ -1070,23 +956,71 @@
"requiredPermissionName": "Abp.Identity.Lockout",
"dataType": "Number",
"selectOptions": {},
"order": 80
"order": 62
},
{
"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,
"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,
"isEncrypted": false,
"mainGroupKey": "Abp.Identity",
"subGroupKey": "Abp.Identity.OrganizationUnits",
"requiredPermissionName": "Abp.Identity.OrganizationUnits",
"dataType": "Number",
"subGroupKey": "Abp.Identity.SignIn",
"requiredPermissionName": "Abp.Identity.SignIn",
"dataType": "Bool",
"selectOptions": {},
"order": 90
"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
}
],
"NotificationTypes": [],

View file

@ -948,12 +948,6 @@
"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",
@ -1929,8 +1923,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.Url.Description",
"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}"
"en": "Url Description",
"tr": "Url Açıklaması"
},
{
"resourceName": "Platform",
@ -1941,8 +1935,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.Sms.PostaGuvercini.Username.Description",
"en": "The user name for the Posta Güvercini service.",
"tr": "Posta Güvercini hizmeti için kullanıcı adı."
"en": "User Name Description",
"tr": "Kullanıcı Adııklaması"
},
{
"resourceName": "Platform",
@ -1953,8 +1947,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.Sms.PostaGuvercini.Password.Description",
"en": "The password for the Posta Güvercini service.",
"tr": "Posta Güvercini hizmeti için parola."
"en": "Password Description",
"tr": "Parola Açıklaması"
},
{
"resourceName": "Platform",
@ -1977,8 +1971,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.WhatsApp.PhoneNumberId.Description",
"en": "The phone number ID for the WhatsApp service.",
"tr": "WhatsApp hizmeti için telefon numarası ID'si."
"en": "PhoneNumberId Description",
"tr": "PhoneNumberId Description"
},
{
"resourceName": "Platform",
@ -1989,8 +1983,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.WhatsApp.TemplateName.Description",
"en": "The name of the template for the WhatsApp service.",
"tr": "WhatsApp hizmeti için şablon adı."
"en": "Template Name Description",
"tr": "Şablon Adııklaması"
},
{
"resourceName": "Platform",
@ -2001,8 +1995,8 @@
{
"resourceName": "Platform",
"key": "App.Sender.WhatsApp.Token.Description",
"en": "The token for the WhatsApp service.",
"tr": "WhatsApp hizmeti için token."
"en": "Token Description",
"tr": "Token Açıklaması"
},
{
"resourceName": "Platform",
@ -2025,14 +2019,20 @@
{
"resourceName": "Platform",
"key": "App.Sender.Rocket.UserId.Description",
"en": "The user ID for the Rocket Chat service.",
"tr": "Rocket Chat hizmeti için kullanıcı ID'si."
"en": "User Id Description",
"tr": "User Id Açıklaması"
},
{
"resourceName": "Platform",
"key": "App.Sender.Rocket.Token",
"en": "Token",
"tr": "Token"
},
{
"resourceName": "Platform",
"key": "App.Sender.Rocket.Token.Description",
"en": "The token for the Rocket Chat service.",
"tr": "Rocket Chat hizmeti için token."
"en": "Token Description",
"tr": "Token Açıklaması"
},
{
"resourceName": "Platform",
@ -2055,8 +2055,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.DefaultFromDisplayName.Description",
"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."
"en": "From Display Name Description",
"tr": "Varsayılan İsim Açıklaması"
},
{
"resourceName": "Platform",
@ -2067,8 +2067,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.DefaultFromAddress.Description",
"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."
"en": "From Address Description",
"tr": "Varsayılan Email Adresi Açıklaması"
},
{
"resourceName": "Platform",
@ -2085,8 +2085,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.Host.Description",
"en": "The host address of the SMTP server.",
"tr": "SMTP sunucusunun ana bilgisayar adresi."
"en": "Host Description",
"tr": "Host Açıklaması"
},
{
"resourceName": "Platform",
@ -2097,8 +2097,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.Port.Description",
"en": "The port number of the SMTP server.",
"tr": "SMTP sunucusunun port numarası."
"en": "Port Description",
"tr": "Port Açıklaması"
},
{
"resourceName": "Platform",
@ -2109,8 +2109,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.UserName.Description",
"en": "The user name for the SMTP server.",
"tr": "SMTP sunucusu için kullanıcı adı."
"en": "User Name Description",
"tr": "Kullanıcı Adııklaması"
},
{
"resourceName": "Platform",
@ -2121,8 +2121,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.Password.Description",
"en": "The password for the SMTP server.",
"tr": "SMTP sunucusu için parola."
"en": "Password Description",
"tr": "Parola Açıklaması"
},
{
"resourceName": "Platform",
@ -2133,8 +2133,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.Domain.Description",
"en": "The domain for the SMTP server.",
"tr": "SMTP sunucusu için alan adı."
"en": "Domain Description",
"tr": "Alan Adııklaması"
},
{
"resourceName": "Platform",
@ -2145,32 +2145,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.Smtp.EnableSsl.Description",
"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."
"en": "Enable Ssl Description",
"tr": "Ssl Etkinleştir Açıklaması"
},
{
"resourceName": "Platform",
@ -2187,8 +2163,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.AWS.Profile.Description",
"en": "The profile for the AWS SES service.",
"tr": "AWS SES hizmeti için profil."
"en": "Profile Description",
"tr": "Profil Açıklaması"
},
{
"resourceName": "Platform",
@ -2199,8 +2175,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.AWS.Region.Description",
"en": "The region of the AWS SES service.",
"tr": "AWS SES hizmetinin bölgesi."
"en": "Region Description",
"tr": "Bölge Açıklaması"
},
{
"resourceName": "Platform",
@ -2211,8 +2187,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.AWS.AccessKey.Description",
"en": "The access key for the AWS SES service.",
"tr": "AWS SES hizmeti için erişim anahtarı."
"en": "Access Key Description",
"tr": "Erişim Anahtarııklaması"
},
{
"resourceName": "Platform",
@ -2223,8 +2199,8 @@
{
"resourceName": "Platform",
"key": "Abp.Mailing.AWS.AccessKeyId.Description",
"en": "The access key ID for the AWS SES service.",
"tr": "AWS SES hizmeti için erişim anahtarı kimliği."
"en": "Access KeyId Description",
"tr": "Erişim Anahtarı Kimliği Açıklaması"
},
{
"resourceName": "Platform",
@ -2247,8 +2223,8 @@
{
"resourceName": "Platform",
"key": "App.SiteManagement.Theme.Style.Description",
"en": "The style for the site theme.",
"tr": "Site temasi için stil."
"en": "Style Description",
"tr": "Stil Açıklaması"
},
{
"resourceName": "Platform",
@ -2265,8 +2241,8 @@
{
"resourceName": "Platform",
"key": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
"en": "The emails sent to new members.",
"tr": "Yeni üelere gönderilen mailler."
"en": "New Member Notification Emails Description",
"tr": "Yeni Üye Bildirim Mailleri Açıklaması"
},
{
"resourceName": "Platform",
@ -2277,8 +2253,8 @@
{
"resourceName": "Platform",
"key": "App.SiteManagement.General.TimedLoginEmails.Description",
"en": "The emails sent for timed login notifications.",
"tr": "Süreli giriş bildirimleri için gönderilen mailler."
"en": "Timed Login Emails Description",
"tr": "Süreli Giriş Mailleri Açıklaması"
},
{
"resourceName": "Platform",
@ -2295,8 +2271,8 @@
{
"resourceName": "Platform",
"key": "Abp.Localization.DefaultLanguage.Description",
"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."
"en": "Default Language Description",
"tr": "Varsayılan Dil Açıklaması"
},
{
"resourceName": "Platform",
@ -2313,8 +2289,8 @@
{
"resourceName": "Platform",
"key": "Abp.Localization.Timezone.Description",
"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."
"en": "Timezone Description",
"tr": "Saat Dilimi Açıklaması"
},
{
"resourceName": "Platform",
@ -2337,8 +2313,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.EnableLocalLogin.Description",
"en": "Enables the local login option on the login page.",
"tr": "Login sayfasında yerel giriş seçeneğini etkinleştirir."
"en": "Authenticate with a local account Description",
"tr": "Yerel bir hesapla kimlik doğrulama açıklaması"
},
{
"resourceName": "Platform",
@ -2349,8 +2325,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.IsSelfRegistrationEnabled.Description",
"en": "Enables the self-registration option on the login page.",
"tr": "Login sayfasında kendi kendine kaydolma seçeneğini etkinleştirir."
"en": "Enable self registration Description",
"tr": "Kendi kendine kaydı etkinleştir Açıklaması"
},
{
"resourceName": "Platform",
@ -2457,8 +2433,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.TwoFactor.Enabled.Description",
"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."
"en": "Two Factor Enabled Description",
"tr": "İki Faktör Ektinleştirme Açıklaması"
},
{
"resourceName": "Platform",
@ -2475,8 +2451,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.Captcha.MaxFailedAccessAttempts.Description",
"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ı."
"en": "Max failed show Captcha Description",
"tr": "Kaç başarısız girişte gösterilecek Açıklaması"
},
{
"resourceName": "Platform",
@ -2487,8 +2463,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.Captcha.EndPoint.Description",
"en": "The URL of the CAPTCHA verification endpoint.",
"tr": "CAPTCHA doğrulama uç noktasının URL'si."
"en": "End Point URL Description",
"tr": "Doğrulama Servis URL Açıklaması"
},
{
"resourceName": "Platform",
@ -2499,8 +2475,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.Captcha.SiteKey.Description",
"en": "The site key for the CAPTCHA service.",
"tr": "CAPTCHA hizmeti için site anahtarı."
"en": "Site Key Description",
"tr": "Site Anahtarııklaması"
},
{
"resourceName": "Platform",
@ -2511,8 +2487,8 @@
{
"resourceName": "Platform",
"key": "Abp.Account.Captcha.SecretKey.Description",
"en": "The private key for the CAPTCHA service.",
"tr": "CAPTCHA hizmeti için özel anahtarı."
"en": "Private Key Description",
"tr": "Özel Anahtarııklaması"
},
{
"resourceName": "Platform",
@ -2838,30 +2814,6 @@
"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",
@ -3357,14 +3309,14 @@
{
"resourceName": "Platform",
"key": "Abp.Identity.Profile.General.RequireVerifiedAccount",
"en": "Require Admin Verified Account",
"tr": "Yönetici Hesap Doğrulaması Yapacak mı?"
"en": "Require Verified Account",
"tr": "Hesap Doğrulaması Zorunlu mu?"
},
{
"resourceName": "Platform",
"key": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description",
"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"
"en": "Require Verified Account Description",
"tr": "Hesap Doğrulama Zorunlu Açıklaması"
},
{
"resourceName": "Platform",
@ -3375,8 +3327,8 @@
{
"resourceName": "Platform",
"key": "Abp.Identity.Profile.General.BlacklistedEmailProviders.Description",
"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"
"en": "Black listed Email Providers Description",
"tr": "Kara Listedeki e-Posta Sağlayıcılarııklaması"
},
{
"resourceName": "Platform",
@ -3642,36 +3594,6 @@
"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",
@ -3765,8 +3687,8 @@
{
"resourceName": "Platform",
"key": "ListForms.ListForm.AddNewRecord",
"en": "Add",
"tr": "Ekle"
"en": "Add New Record",
"tr": "Yeni Kayıt Ekle"
},
{
"resourceName": "Platform",
@ -4184,9 +4106,9 @@
},
{
"resourceName": "Platform",
"key": "ListForms.ListForm.NoteModal.Type.Workflow",
"en": "Workflow",
"tr": "Akış"
"key": "ListForms.ListForm.NoteModal.Type.Activity",
"en": "Activity",
"tr": "Aktivite"
},
{
"resourceName": "Platform",
@ -7392,24 +7314,6 @@
"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",
@ -10336,7 +10240,7 @@
"resourceName": "Platform",
"key": "App.About",
"tr": "Hakkımızda",
"en": "About"
"en": "About Us"
},
{
"resourceName": "Platform",
@ -13952,12 +13856,6 @@
"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",
@ -16766,24 +16664,6 @@
"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",
@ -17522,12 +17402,6 @@
"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",
@ -17768,12 +17642,6 @@
"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",
@ -17798,24 +17666,6 @@
"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",
@ -18116,12 +17966,6 @@
"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",
@ -19070,12 +18914,6 @@
"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",
@ -19321,84 +19159,6 @@
"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"
}
]
}

View file

@ -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, 710, true, true, true, true, false),
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 600, 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, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=8, DataField="Password", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
]}
}),
@ -980,7 +980,6 @@ 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),
@ -1541,7 +1540,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
}),
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
new() { FieldName = "Status", FieldDbType = DbType.String, Value = "active", CustomValueType = FieldCustomValueTypeEnum.Value },
new() { FieldName = "Status", FieldDbType = DbType.String, Value = "Aktif", CustomValueType = FieldCustomValueTypeEnum.Value },
}),
CommandColumnJson = JsonSerializer.Serialize(new CommandColumnDto[] {
new() {
@ -1639,8 +1638,8 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
DisplayExpr = "name",
ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
new () { Key= "active", Name= "Aktif" },
new () { Key= "passive", Name= "Pasif" },
new () { Key="Aktif", Name="Aktif" },
new () { Key="Pasif", Name="Pasif" },
}),
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,
@ -3045,7 +3044,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
new () { Key= "active", Name= "Aktif" },
new () { Key= "passive", Name= "Pasif" },
new () { Key= "passive", Name= "Kapalı" },
}),
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,

View file

@ -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\" IN @Id";
return $"UPDATE \"{TableNameResolver.GetFullTableName(tableName)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";
}
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid, string newId = "@NEWID") => JsonSerializer.Serialize(new FieldsDefaultValue[]

View file

@ -104,6 +104,7 @@ 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 =
@ -118,7 +119,6 @@ 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, 550, true, true, true, false, false),
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 300, true, true, true, false, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
new() {
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=[
@ -4025,8 +4025,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
HeaderFilterJson = DefaultHeaderFilterJson,
SearchPanelJson = DefaultSearchPanelJson,
GroupPanelJson = DefaultGroupPanelJson,
SelectionJson = DefaultSelectionSingleJson,
ColumnOptionJson = DefaultColumnOptionJson(),
SelectionJson = DefaultSelectionMultipleJson,
ColumnOptionJson = DefaultColumnOptionJson(false),
PermissionJson = DefaultPermissionJson(listFormName),
PagerOptionJson = DefaultPagerOptionJson,
EditingOptionJson = DefaultEditingOptionJson(listFormName, 950, 650, true, true, true, true, false),
@ -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, 550, true, true, true, true, false),
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 400, true, true, true, true, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new() {
@ -5477,10 +5477,9 @@ 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 = "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 }
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 }
]
}
}),
@ -5542,33 +5541,6 @@ 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,
@ -5576,7 +5548,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
FieldName = "ComponentPath",
CaptionName = "App.Listform.ListformField.ComponentPath",
Width = 0,
ListOrderNo = 5,
ListOrderNo = 4,
Visible = true,
IsActive = true,
@ -5593,7 +5565,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
FieldName = "RouteType",
CaptionName = "App.Listform.ListformField.RouteType",
Width = 0,
ListOrderNo = 6,
ListOrderNo = 5,
Visible = true,
IsActive = true,
@ -5621,7 +5593,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
FieldName = "Authority",
CaptionName = "App.Listform.ListformField.Authority",
Width = 0,
ListOrderNo = 7,
ListOrderNo = 6,
Visible = true,
IsActive = true,

View file

@ -14,7 +14,6 @@ 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; }
@ -80,7 +79,6 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
await _routeRepository.InsertAsync(new Route(
item.Key,
item.Path,
item.ComponentType,
item.ComponentPath,
item.RouteType,
item.Authority ?? []

View file

@ -1,17 +1,15 @@
{
"Routes": [
{
"key": "roleListComponent",
"key": "dynamic.RoleListComponent",
"path": "/admin/RoleListComponent",
"componentType": "dynamic",
"componentPath": "RoleListComponent",
"componentPath": "dynamic:RoleListComponent",
"routeType": "protected",
"authority": []
},
{
"key": "home",
"path": "/home",
"componentType": "normal",
"componentPath": "@/views/public/Home",
"routeType": "public",
"authority": []
@ -19,7 +17,6 @@
{
"key": "about",
"path": "/about",
"componentType": "normal",
"componentPath": "@/views/public/About",
"routeType": "public",
"authority": []
@ -27,7 +24,6 @@
{
"key": "products",
"path": "/products",
"componentType": "normal",
"componentPath": "@/views/public/Products",
"routeType": "public",
"authority": []
@ -35,7 +31,6 @@
{
"key": "checkout",
"path": "/checkout",
"componentType": "normal",
"componentPath": "@/views/public/Checkout",
"routeType": "public",
"authority": []
@ -43,7 +38,6 @@
{
"key": "payment",
"path": "/payment",
"componentType": "normal",
"componentPath": "@/views/public/Payment",
"routeType": "public",
"authority": []
@ -51,7 +45,6 @@
{
"key": "success",
"path": "/success",
"componentType": "normal",
"componentPath": "@/views/public/Success",
"routeType": "public",
"authority": []
@ -59,7 +52,6 @@
{
"key": "services",
"path": "/services",
"componentType": "normal",
"componentPath": "@/views/public/Services",
"routeType": "public",
"authority": []
@ -67,7 +59,6 @@
{
"key": "blog",
"path": "/blog",
"componentType": "normal",
"componentPath": "@/views/public/Blog",
"routeType": "public",
"authority": []
@ -75,7 +66,6 @@
{
"key": "blogDetail",
"path": "/blog/:id",
"componentType": "normal",
"componentPath": "@/views/public/BlogDetail",
"routeType": "public",
"authority": []
@ -83,7 +73,6 @@
{
"key": "demo",
"path": "/demo",
"componentType": "normal",
"componentPath": "@/views/public/Demo",
"routeType": "public",
"authority": []
@ -91,7 +80,6 @@
{
"key": "contact",
"path": "/contact",
"componentType": "normal",
"componentPath": "@/views/public/Contact",
"routeType": "public",
"authority": []
@ -99,7 +87,6 @@
{
"key": "access-denied",
"path": "/access-denied",
"componentType": "normal",
"componentPath": "@/views/AccessDenied",
"routeType": "public",
"authority": []
@ -107,7 +94,6 @@
{
"key": "login",
"path": "/login",
"componentType": "normal",
"componentPath": "@/views/auth/Login",
"routeType": "authenticated",
"authority": []
@ -115,7 +101,6 @@
{
"key": "register",
"path": "/register",
"componentType": "normal",
"componentPath": "@/views/auth/Register",
"routeType": "authenticated",
"authority": []
@ -123,7 +108,6 @@
{
"key": "forgotPassword",
"path": "/forgot-password",
"componentType": "normal",
"componentPath": "@/views/auth/ForgotPassword",
"routeType": "authenticated",
"authority": []
@ -131,7 +115,6 @@
{
"key": "resetPassword",
"path": "/reset-password",
"componentType": "normal",
"componentPath": "@/views/auth/ResetPassword",
"routeType": "authenticated",
"authority": []
@ -139,7 +122,6 @@
{
"key": "sendConfirmationCode",
"path": "/confirm",
"componentType": "normal",
"componentPath": "@/views/auth/SendConfirmationCode",
"routeType": "authenticated",
"authority": []
@ -147,7 +129,6 @@
{
"key": "sendExtendLogin",
"path": "/extend-login",
"componentType": "normal",
"componentPath": "@/views/auth/ExtendLogin",
"routeType": "authenticated",
"authority": []
@ -155,7 +136,6 @@
{
"key": "verifyConfirmationCode",
"path": "/confirm/:userId/:token",
"componentType": "normal",
"componentPath": "@/views/auth/VerifyConfirmationCode",
"routeType": "authenticated",
"authority": []
@ -163,7 +143,6 @@
{
"key": "admin.dashboard",
"path": "/admin/dashboard",
"componentType": "normal",
"componentPath": "@/views/Dashboard",
"routeType": "protected",
"authority": []
@ -171,7 +150,6 @@
{
"key": "admin.menuManager",
"path": "/admin/menuManager",
"componentType": "normal",
"componentPath": "@/views/menu/MenuManager",
"routeType": "protected",
"authority": ["App.Menus.Manager"]
@ -179,7 +157,6 @@
{
"key": "admin.listFormManagement.wizard",
"path": "/admin/listform/wizard",
"componentType": "normal",
"componentPath": "@/views/admin/listForm/wizard/Wizard",
"routeType": "protected",
"authority": ["App.Listforms.Wizard"]
@ -187,7 +164,6 @@
{
"key": "admin.listFormManagement.wizardManager",
"path": "/admin/listform/wizardManager",
"componentType": "normal",
"componentPath": "@/views/admin/listForm/wizard/WizardFileManager",
"routeType": "protected",
"authority": ["App.Listforms.Wizard"]
@ -195,7 +171,6 @@
{
"key": "admin.listFormManagement.edit",
"path": "/admin/listform/edit/:listFormCode",
"componentType": "normal",
"componentPath": "@/views/admin/listForm/edit/FormEdit",
"routeType": "protected",
"authority": []
@ -203,7 +178,6 @@
{
"key": "admin.forumManagement",
"path": "/admin/forumManagement",
"componentType": "normal",
"componentPath": "@/views/forum/Management",
"routeType": "protected",
"authority": ["App.ForumManagement"]
@ -211,7 +185,6 @@
{
"key": "admin.ai",
"path": "/admin/ai",
"componentType": "normal",
"componentPath": "@/views/ai/Assistant",
"routeType": "protected",
"authority": ["App.Definitions.AiBot.Asistant"]
@ -219,7 +192,6 @@
{
"key": "admin.profile.general",
"path": "/admin/profile/general",
"componentType": "normal",
"componentPath": "@/views/admin/profile/Profile",
"routeType": "protected",
"authority": []
@ -227,7 +199,6 @@
{
"key": "admin.profile.password",
"path": "/admin/profile/password",
"componentType": "normal",
"componentPath": "@/views/admin/profile/Profile",
"routeType": "protected",
"authority": []
@ -235,7 +206,6 @@
{
"key": "admin.profile.notificationSettings",
"path": "/admin/profile/notification-settings",
"componentType": "normal",
"componentPath": "@/views/admin/profile/Profile",
"routeType": "protected",
"authority": []
@ -243,7 +213,6 @@
{
"key": "admin.activityLog",
"path": "/admin/activityLog",
"componentType": "normal",
"componentPath": "@/views/admin/activityLog/ActivityLog",
"routeType": "protected",
"authority": []
@ -251,7 +220,6 @@
{
"key": "admin.changeLog",
"path": "/admin/changeLog",
"componentType": "normal",
"componentPath": "@/views/version/ChangeLog",
"routeType": "protected",
"authority": []
@ -259,7 +227,6 @@
{
"key": "admin.settings",
"path": "/admin/settings",
"componentType": "normal",
"componentPath": "@/views/settings/Settings",
"routeType": "protected",
"authority": ["App.Setting"]
@ -267,7 +234,6 @@
{
"key": "admin.identity.user.detail",
"path": "/admin/users/detail/:userId",
"componentType": "normal",
"componentPath": "@/views/admin/user-management/Details",
"routeType": "protected",
"authority": ["AbpIdentity.Users.Update"]
@ -275,7 +241,6 @@
{
"key": "admin.identity.ous",
"path": "/admin/ous",
"componentType": "normal",
"componentPath": "@/views/admin/organization-unit/OrganizationUnits",
"routeType": "protected",
"authority": ["Abp.Identity.OrganizationUnits"]
@ -283,7 +248,6 @@
{
"key": "admin.hr.organization",
"path": "/admin/organization",
"componentType": "normal",
"componentPath": "@/views/admin/hr/OrgChart",
"routeType": "protected",
"authority": ["App.Definitions.Department"]
@ -291,7 +255,6 @@
{
"key": "admin.forum",
"path": "/admin/forum",
"componentType": "normal",
"componentPath": "@/views/forum/Forum",
"routeType": "protected",
"authority": ["App.ForumManagement.Publish"]
@ -299,7 +262,6 @@
{
"key": "admin.list",
"path": "/admin/list/:listFormCode",
"componentType": "normal",
"componentPath": "@/views/list/List",
"routeType": "protected",
"authority": []
@ -307,7 +269,6 @@
{
"key": "admin.formNew",
"path": "/admin/form/:listFormCode",
"componentType": "normal",
"componentPath": "@/views/form/FormNew",
"routeType": "protected",
"authority": []
@ -315,7 +276,6 @@
{
"key": "admin.formView",
"path": "/admin/form/:listFormCode/:id",
"componentType": "normal",
"componentPath": "@/views/form/FormView",
"routeType": "protected",
"authority": []
@ -323,7 +283,6 @@
{
"key": "admin.formEdit",
"path": "/admin/form/:listFormCode/:id/edit",
"componentType": "normal",
"componentPath": "@/views/form/FormEdit",
"routeType": "protected",
"authority": []
@ -331,7 +290,6 @@
{
"key": "admin.chart",
"path": "/admin/chart/:listFormCode",
"componentType": "normal",
"componentPath": "@/views/list/Chart",
"routeType": "protected",
"authority": []
@ -339,7 +297,6 @@
{
"key": "admin.sqlQueryManager",
"path": "/admin/sqlQueryManager",
"componentType": "normal",
"componentPath": "@/views/developerKit/SqlQueryManager",
"routeType": "protected",
"authority": ["App.SqlQueryManager"]
@ -347,7 +304,6 @@
{
"key": "admin.developerkit.endpoints",
"path": "/admin/developerkit/endpoints",
"componentType": "normal",
"componentPath": "@/views/developerKit/CrudEndpointManager",
"routeType": "protected",
"authority": ["App.DeveloperKit.CrudEndpoints"]
@ -355,7 +311,6 @@
{
"key": "admin.developerkit.dynamic-services",
"path": "/admin/developerkit/dynamic-services",
"componentType": "normal",
"componentPath": "@/views/developerKit/DynamicServiceManager",
"routeType": "protected",
"authority": ["App.DeveloperKit.DynamicServices"]
@ -363,7 +318,6 @@
{
"key": "admin.developerkit.dynamic-services.new",
"path": "/admin/developerkit/dynamic-services/new",
"componentType": "normal",
"componentPath": "@/views/developerKit/DynamicServiceEditor",
"routeType": "protected",
"authority": ["App.DeveloperKit.DynamicServices"]
@ -371,7 +325,6 @@
{
"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"]
@ -379,7 +332,6 @@
{
"key": "admin.developerkit.components",
"path": "/admin/developerkit/components",
"componentType": "normal",
"componentPath": "@/views/developerKit/ComponentManagerPage",
"routeType": "protected",
"authority": ["App.DeveloperKit.Components"]
@ -387,7 +339,6 @@
{
"key": "admin.developerkit.components.new",
"path": "/admin/developerkit/components/new",
"componentType": "normal",
"componentPath": "@/views/developerKit/ComponentEditorPage",
"routeType": "protected",
"authority": ["App.DeveloperKit.Components"]
@ -395,7 +346,6 @@
{
"key": "admin.developerkit.components.view",
"path": "/admin/developerkit/components/view/:id",
"componentType": "normal",
"componentPath": "@/views/developerKit/ComponentEditorPage",
"routeType": "protected",
"authority": ["App.DeveloperKit.Components"]
@ -403,7 +353,6 @@
{
"key": "admin.developerkit.components.edit",
"path": "/admin/developerkit/components/edit/:id",
"componentType": "normal",
"componentPath": "@/views/developerKit/ComponentCodeLayout",
"routeType": "protected",
"authority": ["App.DeveloperKit.Components"]
@ -411,7 +360,6 @@
{
"key": "admin.fileManagement",
"path": "/admin/files",
"componentType": "normal",
"componentPath": "@/views/admin/files/FileManager",
"routeType": "protected",
"authority": ["App.Files"]
@ -419,7 +367,6 @@
{
"key": "admin.devexpressReportView",
"path": "/admin/reports/:id/view",
"componentType": "normal",
"componentPath": "@/views/report/DevexpressReportViewer",
"routeType": "protected",
"authority": []
@ -427,7 +374,6 @@
{
"key": "admin.devexpressReportDesigner",
"path": "/admin/reports/:id/design",
"componentType": "normal",
"componentPath": "@/views/report/DevexpressReportDesigner",
"routeType": "protected",
"authority": []
@ -435,7 +381,6 @@
{
"key": "homeDesigner",
"path": "/admin/public/home/designer",
"componentType": "normal",
"componentPath": "@/views/public/Home",
"routeType": "protected",
"authority": ["App.Home"]
@ -443,7 +388,6 @@
{
"key": "aboutDesigner",
"path": "/admin/public/about/designer",
"componentType": "normal",
"componentPath": "@/views/public/About",
"routeType": "protected",
"authority": ["App.About"]
@ -451,7 +395,6 @@
{
"key": "servicesDesigner",
"path": "/admin/public/services/designer",
"componentType": "normal",
"componentPath": "@/views/public/Services",
"routeType": "protected",
"authority": ["App.Services"]
@ -459,7 +402,6 @@
{
"key": "contactDesigner",
"path": "/admin/public/contact/designer",
"componentType": "normal",
"componentPath": "@/views/public/Contact",
"routeType": "protected",
"authority": ["App.Contact"]
@ -467,7 +409,6 @@
{
"key": "admin.videoroom.dashboard",
"path": "/admin/videoroom/dashboard",
"componentType": "normal",
"componentPath": "@/views/admin/videoroom/Dashboard",
"routeType": "protected",
"authority": ["App.Videoroom.Dashboard"]
@ -475,7 +416,6 @@
{
"key": "admin.videoroom.list",
"path": "/admin/videoroom/list",
"componentType": "normal",
"componentPath": "@/views/admin/videoroom/RoomList",
"routeType": "protected",
"authority": ["App.Videoroom.List"]
@ -483,7 +423,6 @@
{
"key": "admin.videoroom.roomdetail",
"path": "/admin/videoroom/room/:id",
"componentType": "normal",
"componentPath": "@/views/admin/videoroom/RoomDetail",
"routeType": "protected",
"authority": ["App.Videoroom.RoomDetail"]

View file

@ -135,10 +135,10 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
Surname = PlatformConsts.AbpIdentity.User.AdminSurNameDefaultValue,
};
adminUser.SetIsVerified(true);
adminUser.SetEmailConfirmed(true);
adminUser.SetIsVerified(true);
adminUser.SetRocketUsername(PlatformConsts.AbpIdentity.User.AdminRocketUsernameDefaultValue);
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, adminUser.PhoneNumberConfirmed);
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, true);
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);

View file

@ -1,25 +0,0 @@
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

View file

@ -1,385 +0,0 @@
{
"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": []
}
}

View file

@ -36,7 +36,6 @@ 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;
@ -52,7 +51,6 @@ 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;
@ -64,7 +62,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
_repoDataSource = repoDataSource;
_repoListForm = repoListForm;
_repoListFormField = repoListFormField;
_repoListFormWorkflow = repoListFormWorkflow;
_logger = logger;
}
@ -137,29 +134,13 @@ 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 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";
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
var nameLangKey = WizardConsts.WizardKey(wizardName);
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
var code = WizardConsts.WizardKey(wizardName);
// Dil - Language Keys
await CreateLangKeyAsync(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
@ -172,10 +153,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
{
await _repoPermGroup.InsertAsync(
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
await EnsureLangKeyAsync(groupName);
else
await CreateLangKeyAsync(groupName, groupName, groupName);
await CreateLangKeyAsync(groupName, groupName, groupName);
}
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
@ -187,35 +165,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 == permCreateName);
var permCreate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermCreate(wizardName));
if (permCreate == null)
permCreate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermCreate(wizardName), permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: true);
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == permUpdateName);
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermUpdate(wizardName));
if (permUpdate == null)
permUpdate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermUpdate(wizardName), permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: true);
var permDelete = existingPerms.FirstOrDefault(a => a.Name == permDeleteName);
var permDelete = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermDelete(wizardName));
if (permDelete == null)
permDelete = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermDelete(wizardName), permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: true);
var permExport = existingPerms.FirstOrDefault(a => a.Name == permExportName);
var permExport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermExport(wizardName));
if (permExport == null)
permExport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermExport(wizardName), permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: true);
var permImport = existingPerms.FirstOrDefault(a => a.Name == permImportName);
var permImport = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermImport(wizardName));
if (permImport == null)
permImport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermImport(wizardName), permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: true);
var permNote = existingPerms.FirstOrDefault(a => a.Name == permNoteName);
var permNote = existingPerms.FirstOrDefault(a => a.Name == WizardConsts.PermNote(wizardName));
if (permNote == null)
permNote = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: true);
Guid.NewGuid(), groupName, WizardConsts.PermNote(wizardName), 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);
@ -241,18 +219,12 @@ 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;
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
? input.MenuParentIcon
: !string.IsNullOrWhiteSpace(input.MenuIcon)
? input.MenuIcon
: WizardConsts.MenuIcon;
await CreateLangKeyAsync(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
await CreateLangKeyAsync(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
menuParent = await _repoMenu.InsertAsync(new Menu
{
Code = input.MenuParentCode,
DisplayName = input.MenuParentCode,
DisplayName = WizardConsts.WizardKeyParent(wizardName),
IsDisabled = false,
Icon = menuParentIcon,
Order = maxRootOrder + 1,
}, autoSave: true);
}
@ -304,7 +276,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
ColSpan = g.ColCount,
ItemType = "group",
Items = g.Items
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
.Where(i => i.DataField != input.KeyFieldName)
.Select((it, ii) => new EditingFormItemDto
{
Order = ii + 1,
@ -317,7 +289,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
})
.ToArray()
})
.Where(g => g.Items.Length > 0)
.ToList();
// ListForm - varsa sil, yeniden ekle
@ -333,12 +304,6 @@ 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
{
@ -346,7 +311,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
PageSize = 10,
ExportJson = WizardConsts.DefaultExportJson,
IsSubForm = false,
ShowNote = input.SubForms.Count > 0 || input.WorkflowCriteria.Count > 0,
ShowNote = input.SubForms.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,
@ -367,7 +332,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
SelectionJson = WizardConsts.DefaultSelectionSingleJson,
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
PermissionJson = WizardConsts.DefaultPermissionJson(code),
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
@ -416,34 +381,6 @@ 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)
@ -457,60 +394,6 @@ 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;
@ -544,16 +427,6 @@ 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);
}
}
}

View file

@ -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" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
<ProjectReference Include="..\Sozsoft.Platform.Application.Contracts\Sozsoft.Platform.Application.Contracts.csproj" />
<ProjectReference Include="..\Sozsoft.Platform.EntityFrameworkCore\Sozsoft.Platform.EntityFrameworkCore.csproj" />
</ItemGroup>

View file

@ -93,13 +93,6 @@ 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";
@ -169,7 +162,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";
@ -773,10 +766,10 @@ public static class PlatformConsts
{
public static class ParameterTypes
{
public const string Static = "Static";
public const string Query = "Query";
public const string Path = "Path";
public const string Body = "Body";
public const string Static = "S";
public const string Query = "Q";
public const string Path = "P";
public const string Body = "B";
}
}

View file

@ -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 string DefaultSelectionSingleJson(string Mode = "none") => JsonSerializer.Serialize(new
public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new
{
Mode = Mode,
Mode = GridOptions.SelectionModeNone,
AllowSelectAll = false
});
@ -134,7 +134,7 @@ public static class WizardConsts
R = permissionName,
U = permissionName + ".Update",
E = true,
I = true,
I = false,
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\" IN @Id";
return $"UPDATE \"{tableName}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";
}
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid)

View file

@ -26,7 +26,7 @@ public class PlatformBackgroundWorkerTemplateDefinitionProvider : TemplateDefini
foreach (var worker in workers.Where(a =>
a.IsActive &&
a.WorkerType == WorkerTypeEnum.MailQueueWorker &&
a.Options != null).ToList().Where(a => !a.Options.IsNullOrWhiteSpace()))
a.Options != null && a.Options != string.Empty).ToList())
{
var Options = JsonSerializer.Deserialize<MailQueueWorkerOptions>(worker.Options);

View file

@ -7,16 +7,14 @@ 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 componentType, string componentPath, string routeType, string[] authority)
public Route(string key, string path, string componentPath, string routeType, string[] authority)
{
Key = key;
Path = path;
ComponentType = componentType;
ComponentPath = componentPath;
RouteType = routeType;
Authority = authority;

View file

@ -6,14 +6,6 @@ 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; }

View file

@ -1,5 +1,4 @@
using System;
using System.Text.Json.Serialization;
using System;
using System.Threading.Tasks;
using Flurl.Http;
using Sozsoft.Platform.Localization;
@ -24,11 +23,6 @@ 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())
@ -52,24 +46,18 @@ public class TurnstileCaptchaManager : PlatformDomainService, ICaptchaManager
if (response != null && response.StatusCode == 200)
{
var result = await response.GetJsonAsync<TurnstileCaptchaVerifyResponse>();
if (result?.Success != true)
var result = await response.GetJsonAsync<dynamic>();
if (!(bool)result.success)
{
if (throwOnFailure)
{
throw new UserFriendlyException(Localizer[PlatformConsts.AbpIdentity.User.CaptchaWrongCode]);
}
}
return result?.Success == true;
return (bool)result.success;
}
return false;
}
private class TurnstileCaptchaVerifyResponse
{
[JsonPropertyName("success")]
public bool Success { get; set; }
}
}

View file

@ -89,9 +89,7 @@ public class DefaultValueManager : PlatformDomainService, IDefaultValueManager
else if (defaultField.Value == PlatformConsts.DefaultValues.Year)
value = Clock.Now.Year;
else if (defaultField.Value == PlatformConsts.DefaultValues.Id)
value = op == OperationEnum.Delete
? keys
: keys?.FirstOrDefault();
value = keys?.FirstOrDefault();
else if (defaultField.Value == PlatformConsts.DefaultValues.NewId)
value = Guid.NewGuid();
else if (defaultField.Value == PlatformConsts.DefaultValues.Selected_Ids)

View file

@ -2,7 +2,6 @@
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;
@ -170,7 +169,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
// oncelik Command alanindadir, dolu ise silme islemi buradaki sorguya yonlendirilir
if (!string.IsNullOrEmpty(command))
{
sql = NormalizeCollectionParameterCommand(command, parameters, dataSourceType);
sql = command;
}
else
{
@ -190,7 +189,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,
};
@ -210,14 +209,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
string where = string.Empty;
if (parameters.Any())
{
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());
where = string.Join(" AND ", parameters.Select(a => $"\"{a.Key}\" IN (@{a.Key})").ToList());
}
else
{
@ -228,7 +220,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",
};
@ -239,78 +231,5 @@ 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);
}
}

View file

@ -465,28 +465,6 @@ 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");

View file

@ -1,23 +0,0 @@
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;
}
}

View file

@ -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" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
</ItemGroup>
</Project>

View file

@ -2,12 +2,8 @@
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;
@ -182,7 +178,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 = CreateDynamicParameters(parameters);
var param = new DynamicParameters(parameters);
var dbConnection = await GetOrCreateConnectionAsync(cs);
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
@ -192,7 +188,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
public virtual async Task<IEnumerable<dynamic>> QueryAsync(string sql, string cs, Dictionary<string, object> parameters = null)
{
var param = CreateDynamicParameters(parameters);
var param = new DynamicParameters(parameters);
var dbConnection = await GetOrCreateConnectionAsync(cs);
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
@ -201,7 +197,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
public virtual async Task<T> QuerySingleAsync<T>(string sql, string cs, Dictionary<string, object> parameters = null)
{
var param = CreateDynamicParameters(parameters);
var param = new DynamicParameters(parameters);
var dbConnection = await GetOrCreateConnectionAsync(cs);
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
@ -210,7 +206,7 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
public virtual async Task<T> ExecuteScalarAsync<T>(string sql, string cs, Dictionary<string, object> parameters = null)
{
var param = CreateDynamicParameters(parameters);
var param = new DynamicParameters(parameters);
var dbConnection = await GetOrCreateConnectionAsync(cs);
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
@ -241,257 +237,13 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
public virtual async Task<int> ExecuteAsync(string sql, string cs, Dictionary<string, object> parameters = null)
{
var param = CreateDynamicParameters(parameters);
var param = new DynamicParameters(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()

View file

@ -254,7 +254,6 @@ 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);
@ -507,7 +506,6 @@ 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 =>

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260606212623_Initial")]
[Migration("20260526185809_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -3486,9 +3486,6 @@ namespace Sozsoft.Platform.Migrations
b.HasKey("Id");
b.HasIndex("ListFormCode", "Title")
.IsUnique();
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
});
@ -4156,10 +4153,6 @@ 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");

View file

@ -1604,7 +1604,6 @@ 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),
@ -3905,12 +3904,6 @@ 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",

View file

@ -3483,9 +3483,6 @@ namespace Sozsoft.Platform.Migrations
b.HasKey("Id");
b.HasIndex("ListFormCode", "Title")
.IsUnique();
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
});
@ -4153,10 +4150,6 @@ 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");

View file

@ -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 Sas_H_Language WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName",
"Sql": "SELECT * FROM Plat_H_Language WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName",
"ParametersJson": [
{
"Type": "Path",
"Type": "P",
"Name": "CultureName",
"DefaultValue": "ar",
"Path": "/dil/:CultureName/"
},
{
"Type": "Static",
"Type": "S",
"Name": "IsEnabled",
"DefaultValue": "true"
}
@ -341,7 +341,7 @@
"PermissionsJson": [
{
"ResourceType": "User",
"ResourceId": "system@sozsoft.com"
"ResourceId": "SYSTEM@SOZSOFT.COM"
}
]
}
@ -429,153 +429,6 @@
]
}
],
"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",
@ -1456,36 +1309,6 @@
"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": [
@ -1494,7 +1317,7 @@
"content": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızıı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,
@ -1505,7 +1328,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,
@ -1516,7 +1339,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
@ -1526,7 +1349,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
@ -1536,7 +1359,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
@ -1710,42 +1533,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
@ -1831,53 +1654,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": [
@ -2058,7 +1881,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,
@ -2073,7 +1896,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,
@ -2088,7 +1911,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,
@ -2099,55 +1922,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
}

View file

@ -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,7 +40,6 @@ 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; }
@ -393,56 +392,6 @@ 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; }
@ -485,14 +434,12 @@ 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;
@ -535,7 +482,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
public TenantDataSeeder(
IClock clock,
ILookupNormalizer lookupNormalizer,
IIdentityUserRepository repositoryUser,
IRepository<Branch, Guid> branchRepository,
IRepository<GlobalSearch, int> globalSearch,
@ -549,7 +495,6 @@ 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,
@ -585,7 +530,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
)
{
_clock = clock;
_lookupNormalizer = lookupNormalizer;
_repositoryUser = repositoryUser;
_branchRepository = branchRepository;
_globalSearch = globalSearch;
@ -604,7 +548,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
_customComponentRepository = customComponentRepository;
_reportCategoriesRepository = reportCategoriesRepository;
_reportTemplatesRepository = reportTemplatesRepository;
_homeRepository = homeRepository;
_servicesRepository = servicesRepository;
_aboutRepository = aboutRepository;
_contactRepository = contactRepository;
@ -905,30 +848,6 @@ 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();
@ -1074,7 +993,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
if (exists)
continue;
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
await _announcementRepository.InsertAsync(new Announcement
{
Title = item.Title,
@ -1148,7 +1067,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
if (exists)
continue;
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
await _socialPostRepository.InsertAsync(new SocialPost(Guid.NewGuid())
{
@ -1238,7 +1157,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
if (exists)
continue;
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
await _socialCommentRepository.InsertAsync(new SocialComment(Guid.NewGuid())
{
UserId = user != null ? user.Id : null,
@ -1258,7 +1177,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
if (exists)
continue;
var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName));
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
await _socialLikeRepository.InsertAsync(new SocialLike(Guid.NewGuid())
{
SocialPostId = post != null ? post.Id : Guid.Empty,
@ -1299,7 +1218,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(_lookupNormalizer.NormalizeName(item.UserName));
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
if (category != null && type != null)
{

View file

@ -133,10 +133,10 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
Surname = PlatformConsts.AbpIdentity.User.AdminSurNameDefaultValue,
};
adminUser.SetIsVerified(true);
adminUser.SetEmailConfirmed(true);
adminUser.SetIsVerified(true);
adminUser.SetRocketUsername(PlatformConsts.AbpIdentity.User.AdminRocketUsernameDefaultValue);
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, adminUser.PhoneNumberConfirmed);
adminUser.SetPhoneNumber(PlatformConsts.AbpIdentity.User.AdminPhoneNumberDefaultValue, true);
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);

View file

@ -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" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.8" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

View file

@ -125,9 +125,10 @@ public class SetupController : ControllerBase
{
try
{
while (await reader.ReadLineAsync(ct) is { } line)
while (!reader.EndOfStream)
{
await Send(level, line);
var line = await reader.ReadLineAsync(ct);
if (line != null) await Send(level, line);
}
}
catch (OperationCanceledException) { }

View file

@ -264,9 +264,10 @@ internal static class SetupAppRunner
{
try
{
while (await reader.ReadLineAsync(ct) is { } line)
while (!reader.EndOfStream)
{
await Send(level, line);
var line = await reader.ReadLineAsync(ct);
if (line != null) await Send(level, line);
}
}
catch (OperationCanceledException) { }

View file

@ -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] = "confirm";
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.PasswordReset] = "reset-password";
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.UserDetail] = "admin/users/detail";
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["MVC"].RootUrl = configuration["App:SelfUrl"];
//options.Applications["MVC"].Urls[PlatformConsts.Urls.EmailConfirmation] = "Account/Confirm";
@ -525,26 +525,10 @@ public class PlatformHttpApiHostModule : AbpModule
app.UseConfiguredEndpoints();
}
public override Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
{
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;
using var scope = context.ServiceProvider.CreateScope();
var initializer = scope.ServiceProvider.GetRequiredService<BackgroundWorkerInitializer>();
await initializer.RunAsync();
}
}

View file

@ -6,7 +6,6 @@
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>Sozsoft.Platform</RootNamespace>
<PreserveCompilationReferences>true</PreserveCompilationReferences>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>

View file

@ -117,38 +117,6 @@ 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;

View file

@ -13,9 +13,6 @@ volumes:
rocket_mongodb_data:
driver: local
n8n_data:
netdataconfig:
netdatalib:
netdatacache:
services:
forgejo:
@ -111,30 +108,3 @@ 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

View file

@ -20,7 +20,6 @@ SUBDOMAINS=(
"sozsoft.com"
"www.sozsoft.com"
"demo.sozsoft.com"
"dashboard.sozsoft.com"
)
echo "Subdomain'ler için SSL sertifikaları alınıyor..."

View file

@ -1,41 +1,6 @@
{
"commit": "dc293fc",
"commit": "0b5eb3d",
"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",

View file

@ -47,30 +47,12 @@
}
.dialog-content {
max-height: calc(100vh - 2rem);
@apply p-6 rounded-lg shadow-xl my-4 relative bg-white dark:bg-gray-800 flex flex-col overflow-hidden;
}
@screen sm {
.dialog-content {
max-height: calc(100vh - 8rem);
@apply my-16;
}
}
.dialog-header,
.dialog-footer {
@apply flex-shrink-0;
}
.dialog-body {
@apply flex-1 min-h-0 overflow-y-auto;
@apply p-6 rounded-lg shadow-xl sm:my-16 relative h-full bg-white dark:bg-gray-800;
}
.dialog-content.maximized {
border-radius: 0 !important;
height: 100vh !important;
max-height: 100vh !important;
margin: 0 !important;
overflow: hidden;
display: flex;

View file

@ -99,9 +99,7 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
{!selectedFile ? (
<div
className={`relative border-2 border-dashed rounded-lg p-3 transition-all duration-200 ${
dragActive
? 'border-blue-400 bg-blue-50 dark:bg-blue-950/30'
: 'border-slate-300 hover:border-slate-400 dark:border-slate-600 dark:hover:border-slate-500'
dragActive ? 'border-blue-400 bg-blue-50' : 'border-slate-300 hover:border-slate-400'
} ${loading ? 'opacity-50 pointer-events-none' : ''}`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
@ -119,39 +117,35 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
<div className="text-center">
<FaUpload
className={`mx-auto h-3 w-3 ${dragActive ? 'text-blue-500 dark:text-blue-400' : 'text-slate-400 dark:text-slate-500'}`}
className={`mx-auto h-3 w-3 ${dragActive ? 'text-blue-500' : 'text-slate-400'}`}
/>
<div className="text-lg font-medium text-slate-700 dark:text-slate-200 mb-2">
<div className="text-lg font-medium text-slate-700 mb-2">
{dragActive
? translate('::App.Listforms.ImportManager.DropHere')
: translate('::App.Listforms.ImportManager.UploadYourFile')}
</div>
<p className="text-slate-500 dark:text-slate-400 mb-4">
<p className="text-slate-500 mb-4">
{translate('::App.Listforms.ImportManager.DragOrClick')}
</p>
<div className="text-sm text-slate-400 dark:text-slate-500">
<div className="text-sm text-slate-400">
{translate('::App.Listforms.ImportManager.SupportedFormats')}{' '}
{acceptedFormats.join(', ')} Max size: {maxSize}MB
</div>
</div>
</div>
) : (
<div className="border border-slate-200 dark:border-slate-700 rounded-lg p-4 dark:bg-slate-900/40">
<div className="border border-slate-200 rounded-lg p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3 min-w-0 flex-1">
<FaFile className="w-8 h-8 text-blue-500 flex-shrink-0" />
<div className="min-w-0 flex-1">
<div className="font-medium text-slate-800 dark:text-slate-100 truncate">
{selectedFile.name}
</div>
<div className="text-sm text-slate-500 dark:text-slate-400">
{formatFileSize(selectedFile.size)}
</div>
<div className="font-medium text-slate-800 truncate">{selectedFile.name}</div>
<div className="text-sm text-slate-500">{formatFileSize(selectedFile.size)}</div>
</div>
</div>
<button
onClick={clearFile}
className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:text-slate-500 dark:hover:text-slate-300 dark:hover:bg-slate-800 rounded-lg transition-colors"
className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded-lg transition-colors"
>
<FaTimes className="w-4 h-4" />
</button>
@ -160,9 +154,9 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
)}
{error && (
<div className="flex items-center space-x-2 p-3 bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900/60 rounded-lg">
<div className="flex items-center space-x-2 p-3 bg-red-50 border border-red-200 rounded-lg">
<FaRegCircle className="w-5 h-5 text-red-500" />
<span className="text-red-700 dark:text-red-300">{error}</span>
<span className="text-red-700">{error}</span>
</div>
)}

View file

@ -195,17 +195,17 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
switch (status) {
case 'uploaded':
case 'executed':
return 'bg-green-50 text-green-700 border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-900/60'
return 'bg-green-50 text-green-700 border-green-200'
case 'executed_with_errors':
return 'bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950/30 dark:text-orange-300 dark:border-orange-900/60'
return 'bg-orange-50 text-orange-700 border-orange-200'
case 'failed':
case 'execute_failed':
return 'bg-red-50 text-red-700 border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-900/60'
return 'bg-red-50 text-red-700 border-red-200'
case 'processing':
case 'validating':
return 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-900/60'
return 'bg-blue-50 text-blue-700 border-blue-200'
default:
return 'bg-yellow-50 text-yellow-700 border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-300 dark:border-yellow-900/60'
return 'bg-yellow-50 text-yellow-700 border-yellow-200'
}
}
@ -269,17 +269,17 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
const getExecuteStatusColor = (status: string) => {
switch (status) {
case 'completed':
return 'text-green-600 dark:text-green-300'
return 'text-green-600'
case 'completed_with_errors':
return 'text-orange-600 dark:text-orange-300'
return 'text-orange-600'
case 'processing':
return 'text-blue-600 dark:text-blue-300'
return 'text-blue-600'
case 'validating':
return 'text-yellow-600 dark:text-yellow-300'
return 'text-yellow-600'
case 'failed':
return 'text-red-600 dark:text-red-300'
return 'text-red-600'
default:
return 'text-slate-600 dark:text-slate-400'
return 'text-slate-600'
}
}
@ -331,7 +331,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
return (
<div className="flex flex-col w-full mt-4">
{/* Navigation Tabs */}
<div className="flex space-x-1 mb-4 bg-white dark:bg-slate-900 rounded-lg p-1 shadow-sm border border-slate-200 dark:border-slate-700 flex-shrink-0">
<div className="flex space-x-1 mb-4 bg-white rounded-lg p-1 shadow-sm border border-slate-200 flex-shrink-0">
{['import', 'preview', 'history'].map((tab) => (
<button
key={tab}
@ -339,7 +339,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
className={`px-3 py-2 rounded-md font-medium transition-all duration-200 flex items-center space-x-2 ${
activeTab === tab
? 'bg-blue-500 text-white shadow-md'
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50 dark:text-slate-300 dark:hover:text-slate-100 dark:hover:bg-slate-800'
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50'
}`}
>
{tab === 'import' && <FaUpload className="w-4 h-4" />}
@ -358,9 +358,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Template Generator - 2/3 width on large screens, full width on mobile */}
<div className="lg:col-span-2">
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
<div className="px-3 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100 flex items-center">
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
<div className="px-3 py-3 border-b flex items-center justify-between">
<h3 className="text-xl font-semibold text-slate-800 flex items-center">
<FaDownload className="w-4 h-4 mr-2" />
{translate('::App.Listforms.ImportManager.TemplateColumns')} (
{editableColumns.length})
@ -371,10 +371,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<button
onClick={() => generateTemplate('excel')}
disabled={generating}
className="flex items-center gap-1.5 px-3 py-1.5 border border-green-200 dark:border-green-900/60 rounded-md hover:border-green-300 hover:bg-green-50 dark:hover:bg-green-950/30 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-slate-900 text-xs"
className="flex items-center gap-1.5 px-3 py-1.5 border border-green-200 rounded-md hover:border-green-300 hover:bg-green-50 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white text-xs"
>
<FaFileExcel className="w-3.5 h-3.5 text-green-500 group-hover:scale-110 transition-transform" />
<span className="font-medium text-slate-700 dark:text-slate-200">
<span className="font-medium text-slate-700">
{translate('::App.Listforms.ImportManager.ExcelTemplate')}
</span>
</button>
@ -382,10 +382,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<button
onClick={() => generateTemplate('csv')}
disabled={generating}
className="flex items-center gap-1.5 px-3 py-1.5 border border-blue-200 dark:border-blue-900/60 rounded-md hover:border-blue-300 hover:bg-blue-50 dark:hover:bg-blue-950/30 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-slate-900 text-xs"
className="flex items-center gap-1.5 px-3 py-1.5 border border-blue-200 rounded-md hover:border-blue-300 hover:bg-blue-50 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white text-xs"
>
<FaFileAlt className="w-3.5 h-3.5 text-blue-500 group-hover:scale-110 transition-transform" />
<span className="font-medium text-slate-700 dark:text-slate-200">
<span className="font-medium text-slate-700">
{translate('::App.Listforms.ImportManager.CsvTemplate')}
</span>
</button>
@ -394,38 +394,38 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<div className="max-h-96 overflow-y-auto">
<table className="w-full">
<thead className="bg-slate-100 dark:bg-slate-800 sticky top-0">
<thead className="bg-slate-100 sticky top-0">
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
{translate('::App.Listform.ListformField.Column')}
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
{translate('::ListForms.ListFormEdit.Type')}
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
{translate('::App.Required')}
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
{translate('::Abp.Mailing.Default')}
</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
<tbody className="divide-y divide-slate-100">
{editableColumns.map((column: any) => (
<tr key={column.fieldName} className="hover:bg-slate-50 dark:hover:bg-slate-800/70">
<td className="px-2 py-2 font-medium text-slate-800 dark:text-slate-100">
<tr key={column.fieldName} className="hover:bg-slate-50">
<td className="px-2 py-2 font-medium text-slate-800">
{column.fieldName}
</td>
<td className="px-4 py-2 text-slate-600 dark:text-slate-300">
<td className="px-4 py-2 text-slate-600">
<span
className={`px-2 py-1 rounded text-xs font-medium ${
column.dataType === 'string'
? 'bg-blue-100 text-blue-800 dark:bg-blue-950/40 dark:text-blue-300'
? 'bg-blue-100 text-blue-800'
: column.dataType === 'number'
? 'bg-green-100 text-green-800 dark:bg-green-950/40 dark:text-green-300'
? 'bg-green-100 text-green-800'
: column.dataType === 'boolean'
? 'bg-purple-100 text-purple-800 dark:bg-purple-950/40 dark:text-purple-300'
: 'bg-orange-100 text-orange-800 dark:bg-orange-950/40 dark:text-orange-300'
? 'bg-purple-100 text-purple-800'
: 'bg-orange-100 text-orange-800'
}`}
>
{column.dataType}
@ -435,16 +435,16 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{column.validationRuleDto.some(
(rule: any) => rule.type === 'required',
) ? (
<span className="text-red-500 dark:text-red-300 font-medium">
<span className="text-red-500 font-medium">
{translate('::App.Listforms.ImportManager.Yes')}
</span>
) : (
<span className="text-slate-400 dark:text-slate-500">
<span className="text-slate-400">
{translate('::App.Listforms.ImportManager.No')}
</span>
)}
</td>
<td className="px-4 py-2 text-slate-600 dark:text-slate-300 text-sm">
<td className="px-4 py-2 text-slate-600 text-sm">
{typeof column.defaultValue === 'object'
? JSON.stringify(column.defaultValue)
: column.defaultValue}
@ -458,7 +458,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{generating && (
<div className="flex items-center justify-center py-4">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500"></div>
<span className="ml-2 text-slate-600 dark:text-slate-400">
<span className="ml-2 text-slate-600">
{translate('::App.Listforms.ImportManager.GeneratingTemplate')}
</span>
</div>
@ -468,8 +468,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{/* File Upload - 1/3 width on large screens, full width on mobile */}
<div className="lg:col-span-1">
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-4 h-full">
<h2 className="text-xl font-semibold text-slate-800 dark:text-slate-100 mb-4 flex items-center">
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-4 h-full">
<h2 className="text-xl font-semibold text-slate-800 mb-4 flex items-center">
<FaUpload className="w-5 h-5 mr-2 text-green-500" />
{translate('::App.Listforms.ImportManager.UploadData')}
</h2>
@ -506,9 +506,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
)}
</div>
) : (
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-12">
<div className="text-center text-slate-500 dark:text-slate-400">
<FaEye className="w-16 h-16 mx-auto mb-4 text-slate-300 dark:text-slate-600" />
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-12">
<div className="text-center text-slate-500">
<FaEye className="w-16 h-16 mx-auto mb-4 text-slate-300" />
<div className="text-xl font-medium mb-2">
{translate('::App.Listforms.ImportManager.NoDataToPreview')}
</div>
@ -520,22 +520,22 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
)}
{activeTab === 'history' && (
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
<h2 className="text-xl font-semibold text-slate-800 dark:text-slate-100 flex items-center">
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
<div className="p-3 border-b border-slate-200">
<h2 className="text-xl font-semibold text-slate-800 flex items-center">
<FaClock className="w-5 h-5 mr-2 text-indigo-500" />
{translate('::App.Listforms.ImportManager.ImportHistory')}
</h2>
</div>
<div className="divide-y divide-slate-100 dark:divide-slate-800">
<div className="divide-y divide-slate-100">
{importHistory.map((session) => (
<div
key={session.id}
className={`p-2 transition-colors border-l-4 ${
currentSession?.id === session.id
? 'bg-blue-50 border-l-blue-500 hover:bg-blue-100 dark:bg-blue-950/30 dark:hover:bg-blue-950/40'
: 'border-l-transparent hover:bg-slate-50 dark:hover:bg-slate-800/70'
? 'bg-blue-50 border-l-blue-500 hover:bg-blue-100'
: 'border-l-transparent hover:bg-slate-50'
}`}
>
<div className="flex items-center justify-between">
@ -543,15 +543,13 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{getStatusIcon(session.status)}
<div className="flex items-center space-x-2">
<div>
<div className="font-medium text-slate-800 dark:text-slate-100">
{session.blobName}
</div>
<div className="text-sm text-slate-500 dark:text-slate-400">
<div className="font-medium text-slate-800">{session.blobName}</div>
<div className="text-sm text-slate-500">
{new Date(session.creationTime).toLocaleString()}
</div>
</div>
{currentSession?.id === session.id && (
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-950/40 dark:text-blue-300">
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{translate('::App.Status.Active')}
</span>
)}
@ -560,7 +558,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<div className="flex items-center space-x-4">
<div className="text-right">
<div className="text-sm font-medium text-slate-800 dark:text-slate-100">
<div className="text-sm font-medium text-slate-800">
{session.totalRows} {translate('::App.Listforms.ImportManager.TotalRows')}
</div>
</div>
@ -578,8 +576,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
onClick={() => toggleSessionExecutes(session.id)}
className={`p-2 rounded-lg transition-colors ${
expandedSessions.has(session.id)
? 'text-red-500 bg-red-50 hover:text-red-600 hover:bg-red-100 dark:bg-red-950/30 dark:text-red-300 dark:hover:bg-red-950/40'
: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:text-slate-500 dark:hover:text-slate-300 dark:hover:bg-slate-800'
? 'text-red-500 bg-red-50 hover:text-red-600 hover:bg-red-100'
: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100'
}`}
title={translate('::App.Listforms.ImportManager.ViewExecutionDetails')}
>
@ -609,7 +607,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
}
}
}}
className="p-2 rounded-lg transition-colors text-slate-400 hover:text-blue-500 hover:bg-blue-50 dark:text-slate-500 dark:hover:text-blue-300 dark:hover:bg-blue-950/30"
className="p-2 rounded-lg transition-colors text-slate-400 hover:text-blue-500 hover:bg-blue-50"
title={translate('::App.Listforms.ImportManager.RefreshExecutionDetails')}
>
<FaSync className="w-4 h-4" />
@ -625,8 +623,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
disabled={currentSession?.id === session.id}
className={`p-2 rounded-lg transition-colors ${
currentSession?.id === session.id
? 'text-slate-300 dark:text-slate-600 cursor-not-allowed'
: 'text-slate-400 hover:text-red-500 hover:bg-red-50 dark:text-slate-500 dark:hover:text-red-300 dark:hover:bg-red-950/30'
? 'text-slate-300 cursor-not-allowed'
: 'text-slate-400 hover:text-red-500 hover:bg-red-50'
}`}
title={
currentSession?.id === session.id
@ -647,10 +645,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
session.status === 'executed_with_errors' ||
session.status === 'failed' ||
session.status === 'execute_failed'
? 'bg-red-50 text-red-700 border border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-900/60'
? 'bg-red-50 text-red-700 border border-red-200'
: session.status === 'uploaded'
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-900/60'
: 'bg-green-50 text-green-700 border border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-900/60'
? 'bg-blue-50 text-blue-700 border border-blue-200'
: 'bg-green-50 text-green-700 border border-green-200'
}`}
>
<span className="mt-0.5 flex-shrink-0">
@ -670,10 +668,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{/* Execute Details Section */}
{expandedSessions.has(session.id) && (
<div className="mt-3 bg-gradient-to-r from-indigo-50 to-blue-50 dark:from-slate-800 dark:to-slate-800 border border-indigo-100 dark:border-slate-700 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div className="mt-3 bg-gradient-to-r from-indigo-50 to-blue-50 border border-indigo-100 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div className="p-3">
{loadingExecutes.has(session.id) ? (
<div className="flex items-center space-x-2 text-slate-500 dark:text-slate-400 py-2">
<div className="flex items-center space-x-2 text-slate-500 py-2">
<FaSync className="w-4 h-4 animate-spin" />
<span className="text-sm">
{translate('::App.Listforms.ImportManager.LoadingExecutionDetails')}
@ -683,38 +681,38 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
sessionExecutes[session.id].length > 0 ? (
<div className="space-y-2">
{sessionExecutes[session.id].map((execute) => (
<div key={execute.id} className="p-3 rounded-lg dark:bg-slate-900/40">
<div key={execute.id} className="p-3 rounded-lg">
<div className="flex items-center justify-between">
{/* Sol: Tarih */}
<div className="flex-shrink-0">
<div className="text-lg text-slate-500 dark:text-slate-400">
<div className="text-lg text-slate-500">
{new Date(execute.creationTime).toLocaleString()}
</div>
</div>
{/* Orta: Executed, Valid, Errors */}
<div className="flex items-center space-x-4 text-xs text-slate-600 dark:text-slate-400">
<div className="flex items-center space-x-4 text-xs text-slate-600">
<div className="text-center">
<div className="font-medium text-slate-800 dark:text-slate-100">
<div className="font-medium text-slate-800">
{execute.execRows}
</div>
<div className="text-slate-500 dark:text-slate-400">
<div className="text-slate-500">
{translate('::App.Listforms.ImportManager.Executed')}
</div>
</div>
<div className="text-center">
<div className="font-medium text-green-600 dark:text-green-300">
<div className="font-medium text-green-600">
{execute.validRows}
</div>
<div className="text-slate-500 dark:text-slate-400">
<div className="text-slate-500">
{translate('::App.Listforms.ImportManager.Valid')}
</div>
</div>
<div className="text-center">
<div className="font-medium text-red-600 dark:text-red-300">
<div className="font-medium text-red-600">
{execute.errorRows}
</div>
<div className="text-slate-500 dark:text-slate-400">
<div className="text-slate-500">
{translate('::App.Listforms.ImportManager.Errors')}
</div>
</div>
@ -739,7 +737,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
<div className="mt-2">
<button
onClick={() => toggleErrors(execute.id)}
className="flex items-center space-x-1 text-xs text-orange-600 hover:text-orange-700 dark:text-orange-300 dark:hover:text-orange-200 font-medium"
className="flex items-center space-x-1 text-xs text-orange-600 hover:text-orange-700 font-medium"
>
{expandedErrors.has(execute.id) ? (
<FaChevronUp className="w-3 h-3" />
@ -753,29 +751,26 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</button>
{expandedErrors.has(execute.id) && (
<div className="mt-2 max-h-48 overflow-y-auto rounded border border-orange-200 bg-orange-50 dark:border-orange-900/60 dark:bg-orange-950/30">
<div className="mt-2 max-h-48 overflow-y-auto rounded border border-orange-200 bg-orange-50">
{parseErrors(execute.errorsJson).length > 0 ? (
<table className="w-full text-xs">
<thead className="bg-orange-100 dark:bg-orange-950/50 sticky top-0">
<thead className="bg-orange-100 sticky top-0">
<tr>
<th className="px-3 py-1 text-left font-medium text-orange-700 dark:text-orange-300 w-16">
<th className="px-3 py-1 text-left font-medium text-orange-700 w-16">
Satır
</th>
<th className="px-3 py-1 text-left font-medium text-orange-700 dark:text-orange-300">
<th className="px-3 py-1 text-left font-medium text-orange-700">
Hata Mesajı
</th>
</tr>
</thead>
<tbody className="divide-y divide-orange-100 dark:divide-orange-900/50">
<tbody className="divide-y divide-orange-100">
{parseErrors(execute.errorsJson).map((err, idx) => (
<tr
key={idx}
className="hover:bg-orange-100 dark:hover:bg-orange-950/50"
>
<td className="px-3 py-1 text-orange-700 dark:text-orange-300 font-medium">
<tr key={idx} className="hover:bg-orange-100">
<td className="px-3 py-1 text-orange-700 font-medium">
{err.row}
</td>
<td className="px-3 py-1 text-slate-700 dark:text-slate-200">
<td className="px-3 py-1 text-slate-700">
{err.message}
</td>
</tr>
@ -783,7 +778,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</tbody>
</table>
) : (
<p className="px-3 py-2 text-orange-600 dark:text-orange-300">
<p className="px-3 py-2 text-orange-600">
Hata detayı mevcut değil.
</p>
)}
@ -795,7 +790,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
))}
</div>
) : (
<div className="text-sm text-slate-500 dark:text-slate-400 py-2">
<div className="text-sm text-slate-500 py-2">
{translate('::App.Listforms.ImportManager.NoExecutionRecords')}
</div>
)}
@ -806,8 +801,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
))}
{importHistory.length === 0 && (
<div className="p-12 text-center text-slate-500 dark:text-slate-400">
<FaClock className="w-12 h-12 mx-auto mb-4 text-slate-300 dark:text-slate-600" />
<div className="p-12 text-center text-slate-500">
<FaClock className="w-12 h-12 mx-auto mb-4 text-slate-300" />
<div className="text-lg font-medium mb-2">
{translate('::App.Listforms.ImportManager.NoImportHistory')}
</div>

View file

@ -100,13 +100,13 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
const getStatusColor = (status: string) => {
switch (status) {
case 'uploaded':
return 'text-green-600 bg-green-50 border-green-200 dark:text-green-300 dark:bg-green-950/30 dark:border-green-900/60'
return 'text-green-600 bg-green-50 border-green-200'
case 'failed':
return 'text-red-600 bg-red-50 border-red-200 dark:text-red-300 dark:bg-red-950/30 dark:border-red-900/60'
return 'text-red-600 bg-red-50 border-red-200'
case 'validating':
return 'text-yellow-600 bg-yellow-50 border-yellow-200 dark:text-yellow-300 dark:bg-yellow-950/30 dark:border-yellow-900/60'
return 'text-yellow-600 bg-yellow-50 border-yellow-200'
default:
return 'text-blue-600 bg-blue-50 border-blue-200 dark:text-blue-300 dark:bg-blue-950/30 dark:border-blue-900/60'
return 'text-blue-600 bg-blue-50 border-blue-200'
}
}
@ -145,14 +145,14 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
}
return (
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
{/* Header */}
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
<div className="p-3 border-b border-slate-200">
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-center gap-4">
{/* Başlık kısmı - Üstte mobile, solda desktop */}
<div className="flex items-center order-1 lg:order-none">
<FaEye className="w-5 h-5 mr-2 text-blue-500" />
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100">
<h3 className="text-xl font-semibold text-slate-800">
{translate('::App.Listforms.ImportManager.ImportPreviewTitle')}
</h3>
</div>
@ -160,9 +160,9 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
{/* İstatistik kartları - Mobile'da alt alta, desktop'ta yan yana */}
<div className="order-3 lg:order-none lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2">
<div className="flex flex-col sm:flex-row justify-center gap-2">
<div className="text-center px-3 py-1 bg-blue-50 dark:bg-blue-950/30 rounded-full border border-blue-200 dark:border-blue-900/60 font-bold text-blue-600 dark:text-blue-300">
<div className="text-center px-3 py-1 bg-blue-50 rounded-full border border-blue-200 font-bold text-blue-600">
{previewData?.rows?.length || session.totalRows || 0}{' '}
<span className="text-xs text-blue-700 dark:text-blue-300">
<span className="text-xs text-blue-700">
{translate('::App.Listforms.ImportManager.TotalRows')}
</span>
</div>
@ -184,16 +184,16 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
{/* Preview Data */}
{previewData && previewData.headers && previewData.headers.length > 0 ? (
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-4">
<div className="p-3 border-b border-slate-200">
<h4 className="font-semibold text-slate-800 mb-4">
{translate('::App.Listforms.ImportManager.DataPreviewTitle')}
</h4>
<div className="overflow-auto border border-slate-200 dark:border-slate-700 rounded-lg max-h-90">
<div className="overflow-auto border border-slate-200 rounded-lg max-h-90">
<table className="w-full text-sm min-w-full">
<thead className="bg-slate-50 dark:bg-slate-800 sticky top-0 z-10">
<thead className="bg-slate-50 sticky top-0 z-10">
<tr>
<th className="px-4 py-2 text-left font-medium text-slate-700 dark:text-slate-200 whitespace-nowrap w-12">
<th className="px-4 py-2 text-left font-medium text-slate-700 whitespace-nowrap w-12">
<input
type="checkbox"
checked={selectAll}
@ -204,19 +204,19 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
{previewData.headers.map((header: string, index: number) => (
<th
key={index}
className="px-4 py-2 text-left font-medium text-slate-700 dark:text-slate-200 whitespace-nowrap"
className="px-4 py-2 text-left font-medium text-slate-700 whitespace-nowrap"
>
{header}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
<tbody className="divide-y divide-slate-100">
{previewData.rows.map((row: any[], rowIndex: number) => (
<tr
key={rowIndex}
className={`hover:bg-slate-50 dark:hover:bg-slate-800/70 ${
selectedRows.includes(rowIndex) ? 'bg-blue-50 dark:bg-blue-950/30' : ''
className={`hover:bg-slate-50 ${
selectedRows.includes(rowIndex) ? 'bg-blue-50' : ''
}`}
>
<td className="px-4 py-2">
@ -230,7 +230,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
{row.map((cell, cellIndex) => (
<td
key={cellIndex}
className="px-4 py-2 text-slate-600 dark:text-slate-300 whitespace-nowrap max-w-xs truncate"
className="px-4 py-2 text-slate-600 whitespace-nowrap max-w-xs truncate"
>
{cell?.toString() || '-'}
</td>
@ -242,25 +242,25 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
</div>
</div>
) : previewData && previewData.headers && previewData.headers.length === 0 ? (
<div className="p-6 border-b border-slate-200 dark:border-slate-700">
<div className="p-6 border-b border-slate-200">
<div className="text-center py-8">
<FaExclamationTriangle className="w-12 h-12 mx-auto mb-4 text-yellow-500" />
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-2">
<h4 className="font-semibold text-slate-800 mb-2">
{translate('::App.Listforms.ImportManager.NoDataFoundTitle')}
</h4>
<p className="text-slate-600 dark:text-slate-400">
<p className="text-slate-600">
{translate('::App.Listforms.ImportManager.NoDataFoundDescription')}
</p>
</div>
</div>
) : (
<div className="p-6 border-b border-slate-200 dark:border-slate-700">
<div className="p-6 border-b border-slate-200">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div>
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-2">
<h4 className="font-semibold text-slate-800 mb-2">
{translate('::App.Listforms.ImportManager.LoadingPreviewTitle')}
</h4>
<p className="text-slate-600 dark:text-slate-400">
<p className="text-slate-600">
{translate('::App.Listforms.ImportManager.LoadingPreviewDescription')}
</p>
</div>
@ -271,8 +271,8 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
<div className="p-3">
{/* Success Message */}
{showSuccessMessage && lastExecutionResult && (
<div className="mb-4 p-4 bg-green-50 dark:bg-green-950/30 border border-green-200 dark:border-green-900/60 rounded-lg">
<div className="flex items-center space-x-2 text-green-700 dark:text-green-300">
<div className="mb-4 p-4 bg-green-50 border border-green-200 rounded-lg">
<div className="flex items-center space-x-2 text-green-700">
<FaCheckCircle className="w-5 h-5" />
<span className="font-medium">
{translate('::App.Listforms.ImportManager.ImportProgress.Status.Uploaded')}{' '}
@ -286,7 +286,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
{selectedRows.length === 0 && (previewData?.rows?.length || 0) > 0 && (
<div className="flex items-center space-x-2 text-orange-600 dark:text-orange-300">
<div className="flex items-center space-x-2 text-orange-600">
<FaExclamationTriangle className="w-5 h-5" />
<span className="font-medium">
{translate('::App.Listforms.ImportManager.SelectRowsWarning')}
@ -295,7 +295,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
)}
{selectedRows.length > 0 && (
<div className="flex items-center space-x-2 text-blue-600 dark:text-blue-300">
<div className="flex items-center space-x-2 text-blue-600">
<FaCheckCircle className="w-5 h-5" />
<span className="font-medium">
{selectedRows.length} {translate('::App.Listforms.ImportManager.RowsSelected')}
@ -304,7 +304,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
)}
{(previewData?.rows?.length || 0) === 0 && (
<div className="flex items-center space-x-2 text-red-600 dark:text-red-300">
<div className="flex items-center space-x-2 text-red-600">
<FaExclamationTriangle className="w-5 h-5" />
<span className="font-medium">
{translate('::App.Listforms.ImportManager.NoRowsAvailable')}
@ -314,7 +314,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
</div>
<div className="flex space-x-3">
<button className="px-4 py-2 text-slate-600 hover:text-slate-800 hover:bg-slate-100 dark:text-slate-300 dark:hover:text-slate-100 dark:hover:bg-slate-800 rounded-lg transition-colors flex items-center space-x-2">
<button className="px-4 py-2 text-slate-600 hover:text-slate-800 hover:bg-slate-100 rounded-lg transition-colors flex items-center space-x-2">
<FaTimes className="w-4 h-4" />
<span>{translate('::Cancel')}</span>
</button>

View file

@ -59,17 +59,15 @@ export const ImportProgress: React.FC<ImportProgressProps> = ({ session }) => {
}
return (
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="text-center space-y-6">
{/* Status Icon */}
<div className="flex justify-center">{getStatusIcon()}</div>
{/* Status Message */}
<div>
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100 mb-2">
{getStatusMessage()}
</h3>
<p className="text-slate-600 dark:text-slate-400">
<h3 className="text-xl font-semibold text-slate-800 mb-2">{getStatusMessage()}</h3>
<p className="text-slate-600">
{translate('::App.Listforms.Status.Processing')}{' '}
{session.blobName}
</p>
@ -77,11 +75,11 @@ export const ImportProgress: React.FC<ImportProgressProps> = ({ session }) => {
{/* Progress Bar */}
<div className="w-full max-w-md mx-auto">
<div className="flex justify-between text-sm text-slate-600 dark:text-slate-400 mb-2">
<div className="flex justify-between text-sm text-slate-600 mb-2">
<span>{translate('::App.Listforms.ImportManager.ImportProgress.ProgressLabel')}</span>
<span>{Math.round(getProgressPercentage())}%</span>
</div>
<div className="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2">
<div className="w-full bg-slate-200 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300 ease-out"
style={{ width: `${getProgressPercentage()}%` }}

View file

@ -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="w-full min-w-[320px] max-w-[360px] md:min-w-[450px]" bodyClass="md:p-5">
<Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-5">
<div className="flex justify-between items-center mb-2">
{!hasSubdomain() && (
<a

View file

@ -121,14 +121,14 @@ const PublicLayout = () => {
onClick={() => setThemeMode(THEME_ENUM.MODE_LIGHT)}
aria-pressed={!isDarkMode}
title="Light mode"
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 ${
className={`inline-flex h-8 items-center gap-1.5 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} />
<span className="hidden xl:inline">{translate('::App.Light')}</span>
{translate('::App.Light')}
</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 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 ${
className={`inline-flex h-8 items-center gap-1.5 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} />
<span className="hidden xl:inline">{translate('::App.Dark')}</span>
{translate('::App.Dark')}
</button>
</div>
)
@ -163,15 +163,11 @@ const PublicLayout = () => {
: 'bg-gray-950/80 backdrop-blur-sm py-4 border-b border-white/5'
}`}
>
<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"
/>
<div className="container mx-auto px-6 flex items-center justify-between">
<Logo mode="dark" />
{/* 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">
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-1">
{navLinks
.filter((l) => !isLoginLink(l.resourceKey))
.map((link) => {
@ -220,7 +216,7 @@ const PublicLayout = () => {
</nav>
{/* Right side: Language + Login */}
<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">
<div className="hidden md:flex items-center gap-3">
<LanguageSelector />
{themeToggle}
<div className="w-px h-5 bg-white/20" />
@ -241,10 +237,9 @@ const PublicLayout = () => {
{/* Mobile Menu Button */}
<button
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"
className="md:hidden 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>
@ -252,88 +247,68 @@ const PublicLayout = () => {
{/* Mobile Navigation */}
<div
className={`lg:hidden overflow-hidden transition-all duration-300 ease-in-out ${
className={`md:hidden overflow-hidden transition-all duration-300 ease-in-out ${
isOpen ? 'max-h-screen opacity-100' : 'max-h-0 opacity-0'
}`}
>
<div className="border-t border-white/10 bg-gray-950/98 backdrop-blur-md">
<div className="bg-gray-950/98 backdrop-blur-md border-t border-white/10">
<div className="container mx-auto px-6 py-3">
<nav className="flex flex-col gap-1">
<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
}
{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={`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'
}`}
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.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>
<div className="mt-1 border-t border-white/10 pt-2 pb-1">
<div className="flex items-center justify-between gap-3">
<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,
) : 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">
<div className="flex items-center justify-between gap-3">
<LanguageSelector />
{themeToggle}
</div>
</div>
</nav>

View file

@ -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 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white ${
className={`w-full font-medium py-3 px-4 rounded-lg transition-colors duration-200 transform ${
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]'

View file

@ -12,6 +12,7 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
const { className, onError, onExpire, onSuccess } = props
return (
<>
<Turnstile
ref={ref}
className={className ?? 'mb-4 mx-auto'}
@ -20,6 +21,7 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
onExpire={onExpire}
onSuccess={onSuccess}
/>
</>
)
})
Captcha.displayName = 'Captcha'

View file

@ -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">{String(i + 1).padStart(4, '0')}</span>
<span className="text-gray-500 mr-2 select-none">[{log.level}]</span>
{log.message}
</div>
))}

View file

@ -1,148 +0,0 @@
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

View file

@ -19,7 +19,6 @@ 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'

View file

@ -28,7 +28,7 @@ const Logo = (props: LogoProps) => {
return (
<div
className={classNames('logo', 'my-2', className)}
className={classNames('logo', 'my-1', className)}
style={{
...style,
...{ width: logoWidth },

View file

@ -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,153 +9,173 @@ 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
const getType = () => {
const { type = DEFAULT_TYPE } = props
if (TYPE_ARRAY.includes(type)) {
return type
}
return DEFAULT_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 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 { clear } = useTimeout(
onClose as () => void,
duration,
(duration as number) > 0
)
}
const alertDefaultClass = 'p-2 relative flex'
const handleClose = (e: MouseEvent<HTMLDivElement>) => {
setDisplay('hiding')
onClose?.(e)
clear()
if (!triggerByToast) {
setTimeout(() => {
setDisplay('hide')
}, 400)
}
}
const alertClass = classNames(
'alert',
alertDefaultClass,
typeMap.backgroundColor,
typeMap.textColor,
!title ? 'font-semibold' : '',
closable ? 'justify-between' : '',
closable && !title ? 'items-center' : '',
rounded && 'rounded-lg',
className,
)
const renderClose = () => {
return (
<div
className="cursor-pointer"
role="presentation"
onClick={(e) => handleClose(e)}
>
{customClose || <CloseButton defaultStyle={false} />}
</div>
)
}
if (display === 'hide') {
return null
}
const alertDefaultClass = 'p-2 relative flex'
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' : '',
)}
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}
>
{title ? <div className={`font-semibold mb-1 ${typeMap.titleColor}`}>{title}</div> : null}
{children}
</div>
</div>
{closable ? renderClose() : null}
</motion.div>
)
<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>
)
})
Alert.displayName = 'Alert'

View file

@ -17,19 +17,6 @@ const DialogContext = createContext<DialogContextValue>({ isMaximized: false })
export const useDialogContext = () => useContext(DialogContext)
const DialogHeader = ({
children,
className,
}: {
children?: ReactNode
className?: string
}) => (
<div className={classNames('dialog-header', className)}>
{children}
</div>
)
DialogHeader.displayName = 'Dialog.Header'
const DialogBody = ({
children,
className,
@ -37,8 +24,14 @@ const DialogBody = ({
children?: ReactNode
className?: string
}) => {
const { isMaximized } = useContext(DialogContext)
return (
<div className={classNames('dialog-body', className)}>
<div
className={classNames(
isMaximized && 'flex-1 min-h-0 flex flex-col overflow-hidden',
className,
)}
>
{children}
</div>
)
@ -52,8 +45,9 @@ const DialogFooter = ({
children?: ReactNode
className?: string
}) => {
const { isMaximized } = useContext(DialogContext)
return (
<div className={classNames('dialog-footer', className)}>
<div className={classNames(isMaximized && 'flex-shrink-0', className)}>
{children}
</div>
)
@ -237,7 +231,13 @@ const Dialog = (props: DialogProps) => {
{closable && !showWindowControls && renderCloseButton}
{closable && showWindowControls && renderWindowControls}
<DialogContext.Provider value={{ isMaximized }}>
{children}
{isMaximized ? (
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
{children}
</div>
) : (
children
)}
</DialogContext.Provider>
</motion.div>
</Modal>
@ -247,13 +247,11 @@ const Dialog = (props: DialogProps) => {
Dialog.displayName = 'Dialog'
type DialogType = typeof Dialog & {
Header: typeof DialogHeader
Body: typeof DialogBody
Footer: typeof DialogFooter
}
const DialogWithSubComponents = Dialog as DialogType
DialogWithSubComponents.Header = DialogHeader
DialogWithSubComponents.Body = DialogBody
DialogWithSubComponents.Footer = DialogFooter

View file

@ -14,7 +14,6 @@ export interface ListFormWizardColumnItemDto {
editorScript: string
colSpan: number
isRequired: boolean
includeInEditingForm: boolean
dbSourceType: number
}
@ -55,7 +54,6 @@ export interface ListFormWizardDto {
languageTextMenuParentTr: string
permissionGroupName: string
menuParentCode: string
menuParentIcon?: string
menuIcon: string
dataSourceCode: string
dataSourceConnectionString: string
@ -115,7 +113,6 @@ export interface WizardSeedFileItemDto {
editorScript: string
colSpan: number
isRequired: boolean
includeInEditingForm?: boolean
dbSourceType: number
turkishCaption?: string
englishCaption?: string

View file

@ -908,7 +908,6 @@ export interface WidgetEditDto {
export interface WorkflowDto {
approvalUserFieldName: string
isFilterUserName: boolean
approvalDateFieldName: string
approvalStatusFieldName: string
approvalDescriptionFieldName: string

View file

@ -4,7 +4,6 @@ export interface RouteDto extends FullAuditedEntityDto<string> {
id: string;
key: string;
path: string;
componentType: string;
componentPath: string;
routeType: string;
authority: string[];

View file

@ -1,5 +1,6 @@
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
@ -7,12 +8,25 @@ 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)!
}
@ -32,8 +46,9 @@ function loadDynamicComponent(
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}`)
@ -41,53 +56,40 @@ function loadDynamicComponent(
}
// Önce manuel registered komponentleri kontrol et
let DynamicComponent = registeredComponents[componentPath]
let DynamicComponent = registeredComponents[componentName]
// Eğer manuel registered'da yoksa, database compiled komponentleri kontrol et
if (
!DynamicComponent &&
isComponentRegistered &&
renderComponent &&
isComponentRegistered(componentPath)
) {
DynamicComponent = (props: any) => renderComponent(componentPath, props) as React.ReactElement
if (!DynamicComponent && isComponentRegistered && renderComponent && isComponentRegistered(componentName)) {
DynamicComponent = (props: any) => renderComponent(componentName, props) as React.ReactElement
}
if (!DynamicComponent) {
if (isComponentRegistered) {
console.log('Database component registry available - checking...')
}
throw new Error(`Dynamic component not found: ${componentPath}`)
throw new Error(`Dynamic component not found: ${componentName}`)
}
// console.log(`Dynamic component loaded: ${componentPath}`)
// console.log(`Dynamic component loaded: ${componentName}`)
// 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(
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 (componentType === 'normal') {
if (isPhysicalComponent(componentPath)) {
return loadPhysicalComponent(componentPath)
} else if (componentType === 'dynamic') {
} else if (isDynamicComponent(componentPath)) {
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)
@ -101,12 +103,13 @@ 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
@ -115,16 +118,11 @@ export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] {
key: route.path,
path: route.path,
getComponent: (registeredComponents, renderComponent, isComponentRegistered) =>
loadComponent(
route.componentType,
route.componentPath,
registeredComponents,
renderComponent,
isComponentRegistered,
),
loadComponent(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),
}))
}

View file

@ -15,37 +15,6 @@ 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()
@ -64,11 +33,7 @@ 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}
@ -120,11 +85,7 @@ 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}
@ -139,9 +100,15 @@ export const DynamicRouter: React.FC = () => {
})}
{/* root redirect */}
<Route path="/" element={<RootRedirect />} />
<Route path="/account/confirm/:userId/:token" element={<LegacyEmailConfirmationRedirect />} />
<Route path="/account/reset-password" element={<LegacyPasswordResetRedirect />} />
<Route
path="/"
element={
<Navigate
to={hasSubdomain() ? ROUTES_ENUM.authenticated.login : ROUTES_ENUM.public.home}
replace
/>
}
/>
{/* public access denied (statik) */}
<Route

View file

@ -52,8 +52,8 @@ export const resetPassword = (userId: string, resetToken: string, password: stri
},
})
export const sendAccountConfirmationCode = (data: any) =>
apiService.fetchData<boolean>({
export const sendAccountConfirmationCode = (data: any) => {
apiService.fetchData({
method: 'POST',
url: 'api/app/platform-account/send-account-confirmation-code',
data: {
@ -61,6 +61,7 @@ export const sendAccountConfirmationCode = (data: any) =>
captchaResponse: data.captchaResponse,
},
})
}
export const verifyAccountConfirmationCode = (userId: string, token: string) =>
apiService.fetchData({
@ -73,7 +74,7 @@ export const verifyAccountConfirmationCode = (userId: string, token: string) =>
})
export const sendExtendLoginRequest = (data: any) =>
apiService.fetchData<boolean>({
apiService.fetchData({
method: 'POST',
url: 'api/app/platform-account/send-extend-login-request',
data: {

View file

@ -1,24 +0,0 @@
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()

View file

@ -11,11 +11,6 @@ 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',
@ -41,21 +36,6 @@ 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',

View file

@ -43,7 +43,6 @@ export interface WorkflowRunResultDto {
currentNodeKind?: string | null
waitingApproval: boolean
completed: boolean
toastMessages?: string[]
}
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {

View file

@ -1,29 +0,0 @@
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)
}

View file

@ -1,6 +1,5 @@
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
import {
sendAccountConfirmationCode,
verifyAccountConfirmationCode,
@ -13,28 +12,15 @@ function useAccount() {
const sendConfirmationCode = async (values: any) => {
try {
const result = await sendAccountConfirmationCode(values)
if (result.data !== true) {
throw new Error('This email is already confirmed or no account was found.')
}
await sendAccountConfirmationCode(values)
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(err)
setError(error?.response?.data?.message || error.toString())
return {
status: 'failed',
message: err,
message: error?.response?.data?.message || error.toString(),
}
}
}
@ -52,7 +38,7 @@ function useAccount() {
setError('')
setMessage('Your account is confirmed')
setTimeout(() => {
navigate(ROUTES_ENUM.authenticated.login)
navigate('/account/login')
}, 3000)
} else {
throw new Error('Invalid token')

View file

@ -328,91 +328,6 @@ 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 || ''
@ -432,8 +347,7 @@ export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm
}
export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput {
const sharedPerson =
item.kind === 'Approval' || item.kind === 'Inform' ? item.approver || '' : ''
const sharedPerson = item.approver || ''
const compareOutcomes = (item.compareOutcomes || [])
.slice(0, 4)
.filter((outcome) => outcome.label?.trim())
@ -590,7 +504,7 @@ export function criteriaSummary(item: WorkflowCriteriaDto) {
if (item.kind === 'Approval' || item.kind === 'Inform') {
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
}
return item.title
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
}
export function targetTitle(criteria: WorkflowCriteriaDto[], id?: string | null) {

View file

@ -46,11 +46,9 @@ const Views = (props: ViewsProps) => {
<ErrorBoundary fallbackRender={fallbackRender}>
<Suspense fallback={<Loading loading={true} />}>
{!!warning?.length && (
<Alert showIcon className="mb-4 text-sm text-left" type="warning">
<Alert showIcon className="mb-4" type="warning">
{warning.map((w, i) => (
<div key={i} className="whitespace-normal break-words">
{w}
</div>
<div key={i}>{w}</div>
))}
</Alert>
)}

View file

@ -456,15 +456,13 @@ 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 dark:bg-gray-900 dark:text-gray-100`}>
<div data-header="" className={`${headerBg} rounded-t-xl px-3 py-2 flex items-center gap-2`}>
{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 dark:bg-gray-900 dark:text-gray-100">
{node.name}
</span>
<span data-node-name="" className="text-white font-semibold text-xs truncate flex-1">{node.name}</span>
{hasChildren && (
<button
data-stop-drag="true"
@ -516,7 +514,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 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-100">
<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">
{node.children.length}
</div>
)}

View file

@ -5,16 +5,15 @@ 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, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui'
import { Button, Card, FormContainer, FormItem, Input, Select } from '@/components/ui'
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
import { bool, object, string } from 'yup'
import { object, string } from 'yup'
import { useStoreState } from '@/store/store'
import { FormEditProps } from './FormEdit'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -113,17 +112,9 @@ 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({
...normalized,
...normalizeCriteria(criteriaForm),
listFormCode: props.listFormCode,
title: uniqueCriteriaTitle(
normalized.kind || '',
currentCriteria,
normalized.id,
preferredTitle,
),
})
setSelectedId('')
})
@ -132,11 +123,9 @@ 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,
})
@ -307,13 +296,10 @@ export function FormTabWorkflow(
}
const schema = object().shape({
workflowDto: object().shape({
approvalUserFieldName: string().required(),
isFilterUserName: bool(),
approvalStatusFieldName: string().required(),
approvalDateFieldName: string(),
approvalDescriptionFieldName: string(),
}),
approvalUserFieldName: string().required(),
approvalStatusFieldName: string().required(),
approvalDateFieldName: string(),
approvalDescriptionFieldName: string(),
})
const initialValues = useStoreState((s) => s.admin.lists.values)
@ -334,7 +320,7 @@ export function FormTabWorkflow(
<Form>
<FormContainer size="sm">
<Card className="my-2">
<div className="grid grid-cols-1 md:grid-cols-5 gap-2">
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
<FormItem
asterisk
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalUserFieldName')}
@ -435,22 +421,9 @@ 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" type="submit" loading={isSubmitting}>
<Button block variant="solid" loading={isSubmitting}>
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</Card>

Some files were not shown because too many files have changed in this diff Show more