Compare commits
No commits in common. "main" and "1.1.04" have entirely different histories.
63 changed files with 423 additions and 2986 deletions
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ namespace Sozsoft.Platform.ListForms;
|
||||||
public class WorkflowDto
|
public class WorkflowDto
|
||||||
{
|
{
|
||||||
public string ApprovalUserFieldName { get; set; }
|
public string ApprovalUserFieldName { get; set; }
|
||||||
public bool IsFilterUserName { get; set; }
|
|
||||||
public string ApprovalDateFieldName { get; set; }
|
public string ApprovalDateFieldName { get; set; }
|
||||||
public string ApprovalStatusFieldName { get; set; }
|
public string ApprovalStatusFieldName { get; set; }
|
||||||
public string ApprovalDescriptionFieldName { get; set; }
|
public string ApprovalDescriptionFieldName { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ public class ListFormWizardDto
|
||||||
|
|
||||||
public string PermissionGroupName { get; set; }
|
public string PermissionGroupName { get; set; }
|
||||||
public string MenuParentCode { get; set; }
|
public string MenuParentCode { get; set; }
|
||||||
public string MenuParentIcon { get; set; }
|
|
||||||
public string MenuIcon { get; set; }
|
public string MenuIcon { get; set; }
|
||||||
public string DataSourceCode { get; set; }
|
public string DataSourceCode { get; set; }
|
||||||
public string DataSourceConnectionString { get; set; }
|
public string DataSourceConnectionString { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ public class WizardColumnItemInputDto
|
||||||
public string EditorScript { get; set; }
|
public string EditorScript { get; set; }
|
||||||
public int ColSpan { get; set; } = 1;
|
public int ColSpan { get; set; } = 1;
|
||||||
public bool IsRequired { get; set; }
|
public bool IsRequired { get; set; }
|
||||||
public bool IncludeInEditingForm { get; set; } = true;
|
|
||||||
public DbType DbSourceType { get; set; } = DbType.String;
|
public DbType DbSourceType { get; set; } = DbType.String;
|
||||||
public string TurkishCaption { get; set; }
|
public string TurkishCaption { get; set; }
|
||||||
public string EnglishCaption { get; set; }
|
public string EnglishCaption { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
public class WorkflowRunResultDto
|
public class WorkflowRunResultDto
|
||||||
|
|
@ -10,6 +8,5 @@ public class WorkflowRunResultDto
|
||||||
public string CurrentNodeKind { get; set; }
|
public string CurrentNodeKind { get; set; }
|
||||||
public bool WaitingApproval { get; set; }
|
public bool WaitingApproval { get; set; }
|
||||||
public bool Completed { get; set; }
|
public bool Completed { get; set; }
|
||||||
public List<string> ToastMessages { get; set; } = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,30 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Sozsoft.Platform.Entities;
|
|
||||||
using Sozsoft.Platform.ListForms;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.AuditLogging;
|
using Volo.Abp.AuditLogging;
|
||||||
using Volo.Abp.Domain.Repositories;
|
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
|
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.AuditLogs;
|
namespace Sozsoft.Platform.AuditLogs;
|
||||||
|
|
||||||
public interface IAuditLogAppService
|
public interface IAuditLogAppService
|
||||||
: ICrudAppService<AuditLogDto, Guid, AuditLogListRequestDto>
|
: ICrudAppService<AuditLogDto, Guid>
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(AppCodes.IdentityManagement.AuditLogs)]
|
[Authorize(AppCodes.IdentityManagement.AuditLogs)]
|
||||||
public class AuditLogAppService : CrudAppService<
|
public class AuditLogAppService
|
||||||
AuditLog,
|
: CrudAppService<AuditLog, AuditLogDto, Guid>
|
||||||
AuditLogDto,
|
, IAuditLogAppService
|
||||||
Guid,
|
|
||||||
AuditLogListRequestDto>, IAuditLogAppService
|
|
||||||
{
|
{
|
||||||
private readonly IRepository<ListForm, Guid> _listFormRepository;
|
public AuditLogAppService(IAuditLogRepository auditLogRepository) : base(auditLogRepository)
|
||||||
|
|
||||||
public AuditLogAppService(
|
|
||||||
IAuditLogRepository auditLogRepository,
|
|
||||||
IRepository<ListForm, Guid> listFormRepository
|
|
||||||
) : base(auditLogRepository)
|
|
||||||
{
|
{
|
||||||
_listFormRepository = listFormRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<AuditLogDto> GetAsync(Guid id)
|
public override async Task<AuditLogDto> GetAsync(Guid id)
|
||||||
|
|
@ -48,30 +35,27 @@ public class AuditLogAppService : CrudAppService<
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogListRequestDto input)
|
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(PagedAndSortedResultRequestDto input)
|
||||||
{
|
{
|
||||||
var query = await Repository.WithDetailsAsync();
|
var query = await CreateFilteredQueryAsync(input);
|
||||||
|
|
||||||
if (!input.ListFormCode.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
var filterRules = await GetListFormFilterRulesAsync(input.ListFormCode);
|
|
||||||
query = ApplyAuditLogActionParametersFilter(query, filterRules, input.EntityId);
|
|
||||||
}
|
|
||||||
else if (!input.EntityId.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
query = query.Where(a => a.Actions.Any(action => action.Parameters.Contains(input.EntityId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalCount = await AsyncExecuter.CountAsync(query);
|
var totalCount = await AsyncExecuter.CountAsync(query);
|
||||||
|
|
||||||
query = ApplySorting(query, input);
|
query = ApplySorting(query, input);
|
||||||
query = ApplyPaging(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
|
// Mapping tek seferde yap
|
||||||
var entityDtos = ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogsWithDetails);
|
var entityDtos = ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogsWithDetails);
|
||||||
|
|
||||||
// EntityChangeCount'u doldur (artık EntityChanges yüklü)
|
// EntityChangeCount'u doldur (artık EntityChanges yüklü)
|
||||||
foreach (var dto in entityDtos)
|
foreach (var dto in entityDtos)
|
||||||
{
|
{
|
||||||
|
|
@ -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
|
// Audit Log kayitlarini gormek istiyoruz fakat degistirmek istemiyoruz
|
||||||
[RemoteService(IsEnabled = false)]
|
[RemoteService(IsEnabled = false)]
|
||||||
public override Task<AuditLogDto> CreateAsync(AuditLogDto input)
|
public override Task<AuditLogDto> CreateAsync(AuditLogDto input)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,10 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Platform.BlobStoring;
|
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Extensions;
|
using Sozsoft.Platform.Extensions;
|
||||||
using Sozsoft.Platform.Identity.Dto;
|
using Sozsoft.Platform.Identity.Dto;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -34,7 +32,6 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
||||||
public IRepository<Department, Guid> departmentRepository { get; }
|
public IRepository<Department, Guid> departmentRepository { get; }
|
||||||
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
||||||
public BlobManager BlobCdnManager { get; }
|
|
||||||
|
|
||||||
public PlatformIdentityAppService(
|
public PlatformIdentityAppService(
|
||||||
IIdentityUserAppService identityUserAppService,
|
IIdentityUserAppService identityUserAppService,
|
||||||
|
|
@ -48,7 +45,6 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
IRepository<WorkHour, Guid> workHourRepository,
|
IRepository<WorkHour, Guid> workHourRepository,
|
||||||
IRepository<Department, Guid> departmentRepository,
|
IRepository<Department, Guid> departmentRepository,
|
||||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||||
BlobManager blobCdnManager,
|
|
||||||
IGuidGenerator guidGenerator
|
IGuidGenerator guidGenerator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -59,7 +55,6 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
this.workHourRepository = workHourRepository;
|
this.workHourRepository = workHourRepository;
|
||||||
this.departmentRepository = departmentRepository;
|
this.departmentRepository = departmentRepository;
|
||||||
this.jobPositionRepository = jobPositionRepository;
|
this.jobPositionRepository = jobPositionRepository;
|
||||||
this.BlobCdnManager = blobCdnManager;
|
|
||||||
this.permissionRepository = permissionRepository;
|
this.permissionRepository = permissionRepository;
|
||||||
this.branchRepository = branchRepository;
|
this.branchRepository = branchRepository;
|
||||||
this.branchUsersRepository = branchUsersRepository;
|
this.branchUsersRepository = branchUsersRepository;
|
||||||
|
|
@ -278,22 +273,6 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
await UserManager.UpdateAsync(user);
|
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()
|
public async Task<List<PermissionDefinitionRecord>> GetPermissionList()
|
||||||
{
|
{
|
||||||
var list = await permissionRepository.GetListAsync();
|
var list = await permissionRepository.GetListAsync();
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
using Volo.Abp.PermissionManagement;
|
using Volo.Abp.PermissionManagement;
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
|
using static Sozsoft.Platform.PlatformConsts;
|
||||||
|
using System.Data;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Sozsoft.Languages;
|
using Sozsoft.Languages;
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
|
|
@ -58,21 +60,10 @@ public class ListFormWizardAppService(
|
||||||
public async Task Create(ListFormWizardDto input)
|
public async Task Create(ListFormWizardDto input)
|
||||||
{
|
{
|
||||||
var wizardName = input.WizardName.Trim();
|
var wizardName = input.WizardName.Trim();
|
||||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
|
||||||
? WizardConsts.WizardKey(wizardName)
|
var nameLangKey = WizardConsts.WizardKey(wizardName);
|
||||||
: input.MenuCode.Trim();
|
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
|
||||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
var code = WizardConsts.WizardKey(wizardName);
|
||||||
? code
|
|
||||||
: input.ListFormCode.Trim();
|
|
||||||
var titleLangKey = $"{listFormCode}.Title";
|
|
||||||
var nameLangKey = code;
|
|
||||||
var descLangKey = $"{listFormCode}.Desc";
|
|
||||||
var permCreateName = $"{code}.Create";
|
|
||||||
var permUpdateName = $"{code}.Update";
|
|
||||||
var permDeleteName = $"{code}.Delete";
|
|
||||||
var permExportName = $"{code}.Export";
|
|
||||||
var permImportName = $"{code}.Import";
|
|
||||||
var permNoteName = $"{code}.Note";
|
|
||||||
|
|
||||||
// Eklenen kayıtları takip et (silme işleminde kullanılır)
|
// Eklenen kayıtları takip et (silme işleminde kullanılır)
|
||||||
var inserted = new WizardInsertedRecordsDto();
|
var inserted = new WizardInsertedRecordsDto();
|
||||||
|
|
@ -87,15 +78,16 @@ public class ListFormWizardAppService(
|
||||||
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
|
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
|
||||||
{
|
{
|
||||||
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
|
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
|
||||||
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
|
await CreateLangKey(groupName, groupName, groupName, inserted);
|
||||||
await EnsureLangKey(groupName, inserted);
|
|
||||||
else
|
|
||||||
await CreateLangKey(groupName, groupName, groupName, inserted);
|
|
||||||
inserted.PermissionGroupNames.Add(groupName);
|
inserted.PermissionGroupNames.Add(groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permission'ları tek seferde kontrol et ve oluştur
|
// 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);
|
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
|
||||||
if (permRead == null)
|
if (permRead == null)
|
||||||
{
|
{
|
||||||
|
|
@ -103,45 +95,45 @@ public class ListFormWizardAppService(
|
||||||
inserted.PermissionNames.Add(permRead.Name);
|
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)
|
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);
|
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)
|
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);
|
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)
|
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);
|
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)
|
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);
|
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)
|
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);
|
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)
|
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);
|
inserted.PermissionNames.Add(permNote.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,18 +161,12 @@ public class ListFormWizardAppService(
|
||||||
if (menuParent == null)
|
if (menuParent == null)
|
||||||
{
|
{
|
||||||
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
||||||
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
|
await CreateLangKey(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
|
||||||
? input.MenuParentIcon
|
|
||||||
: !string.IsNullOrWhiteSpace(input.MenuIcon)
|
|
||||||
? input.MenuIcon
|
|
||||||
: WizardConsts.MenuIcon;
|
|
||||||
await CreateLangKey(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr, inserted);
|
|
||||||
menuParent = await repoMenu.InsertAsync(new Menu
|
menuParent = await repoMenu.InsertAsync(new Menu
|
||||||
{
|
{
|
||||||
Code = input.MenuParentCode,
|
Code = input.MenuParentCode,
|
||||||
DisplayName = input.MenuParentCode,
|
DisplayName = WizardConsts.WizardKeyParent(wizardName),
|
||||||
IsDisabled = false,
|
IsDisabled = false,
|
||||||
Icon = menuParentIcon,
|
|
||||||
Order = maxRootOrder + 1,
|
Order = maxRootOrder + 1,
|
||||||
}, autoSave: false);
|
}, autoSave: false);
|
||||||
inserted.MenuCodes.Add(input.MenuParentCode);
|
inserted.MenuCodes.Add(input.MenuParentCode);
|
||||||
|
|
@ -232,7 +218,7 @@ public class ListFormWizardAppService(
|
||||||
ColSpan = g.ColCount,
|
ColSpan = g.ColCount,
|
||||||
ItemType = "group",
|
ItemType = "group",
|
||||||
Items = g.Items
|
Items = g.Items
|
||||||
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
|
.Where(i => i.DataField != input.KeyFieldName)
|
||||||
.Select((it, ii) => new EditingFormItemDto
|
.Select((it, ii) => new EditingFormItemDto
|
||||||
{
|
{
|
||||||
Order = ii + 1,
|
Order = ii + 1,
|
||||||
|
|
@ -245,7 +231,6 @@ public class ListFormWizardAppService(
|
||||||
})
|
})
|
||||||
.ToArray()
|
.ToArray()
|
||||||
})
|
})
|
||||||
.Where(g => g.Items.Length > 0)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
//ListForm - varsa sil, yeniden ekle
|
//ListForm - varsa sil, yeniden ekle
|
||||||
|
|
@ -272,15 +257,14 @@ public class ListFormWizardAppService(
|
||||||
var isCreated = tableColumns.Contains("CreatorId");
|
var isCreated = tableColumns.Contains("CreatorId");
|
||||||
input.Workflow ??= new WorkflowDto();
|
input.Workflow ??= new WorkflowDto();
|
||||||
input.Workflow.Criteria = input.WorkflowCriteria;
|
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
|
||||||
|
|
||||||
await repoListForm.InsertAsync(new ListForm
|
var listForm = await repoListForm.InsertAsync(new ListForm
|
||||||
{
|
{
|
||||||
ListFormType = ListFormTypeEnum.List,
|
ListFormType = ListFormTypeEnum.List,
|
||||||
PageSize = 10,
|
PageSize = 10,
|
||||||
ExportJson = WizardConsts.DefaultExportJson,
|
ExportJson = WizardConsts.DefaultExportJson,
|
||||||
IsSubForm = false,
|
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),
|
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler),
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
ListFormCode = input.ListFormCode,
|
ListFormCode = input.ListFormCode,
|
||||||
|
|
@ -296,12 +280,12 @@ public class ListFormWizardAppService(
|
||||||
KeyFieldName = input.KeyFieldName,
|
KeyFieldName = input.KeyFieldName,
|
||||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
||||||
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
||||||
SortMode = PlatformConsts.GridOptions.SortModeSingle,
|
SortMode = GridOptions.SortModeSingle,
|
||||||
FilterRowJson = WizardConsts.DefaultFilterRowJson,
|
FilterRowJson = WizardConsts.DefaultFilterRowJson,
|
||||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
||||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
||||||
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
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(),
|
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
||||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
||||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
||||||
|
|
@ -411,65 +395,13 @@ public class ListFormWizardAppService(
|
||||||
{
|
{
|
||||||
return workflow != null && (
|
return workflow != null && (
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
||||||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
||||||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
|
||||||
criteria.Count > 0
|
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>
|
/// <summary>
|
||||||
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
||||||
/// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar.
|
/// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar.
|
||||||
|
|
@ -745,16 +677,4 @@ public class ListFormWizardAppService(
|
||||||
|
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<LanguageKey> EnsureLangKey(string key, WizardInsertedRecordsDto inserted = null)
|
|
||||||
{
|
|
||||||
var res = PlatformConsts.AppName;
|
|
||||||
|
|
||||||
var existing = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == key);
|
|
||||||
if (existing != null) return existing;
|
|
||||||
|
|
||||||
existing = await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key }, autoSave: true);
|
|
||||||
inserted?.LanguageKeys.Add(key);
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,10 @@ using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Sozsoft.Platform.Data.Seeds;
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.ListForms.Select;
|
using Sozsoft.Platform.ListForms.Select;
|
||||||
using Sozsoft.Platform.Localization;
|
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using Sozsoft.Sender.Mail;
|
using Sozsoft.Sender.Mail;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
|
|
@ -31,7 +29,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
private const string SystemApprovalDescription = "Sistem tarafından otomatik olarak onaylandı.";
|
private const string SystemApprovalDescription = "Sistem tarafından otomatik olarak onaylandı.";
|
||||||
|
|
||||||
private readonly IRepository<ListFormWorkflow, string> criteriaRepository;
|
private readonly IRepository<ListFormWorkflow, string> criteriaRepository;
|
||||||
private readonly IRepository<Note, Guid> noteRepository;
|
|
||||||
private readonly IListFormManager listFormManager;
|
private readonly IListFormManager listFormManager;
|
||||||
private readonly IListFormAuthorizationManager authManager;
|
private readonly IListFormAuthorizationManager authManager;
|
||||||
private readonly IListFormSelectAppService listFormSelectAppService;
|
private readonly IListFormSelectAppService listFormSelectAppService;
|
||||||
|
|
@ -39,22 +36,18 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
private readonly IdentityUserManager identityUserManager;
|
private readonly IdentityUserManager identityUserManager;
|
||||||
private readonly ISozsoftEmailSender erpEmailSender;
|
private readonly ISozsoftEmailSender erpEmailSender;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
private readonly IStringLocalizer<PlatformResource> localizer;
|
|
||||||
|
|
||||||
public ListFormWorkflowAppService(
|
public ListFormWorkflowAppService(
|
||||||
IRepository<ListFormWorkflow, string> criteriaRepository,
|
IRepository<ListFormWorkflow, string> criteriaRepository,
|
||||||
IRepository<Note, Guid> noteRepository,
|
|
||||||
IListFormManager listFormManager,
|
IListFormManager listFormManager,
|
||||||
IListFormAuthorizationManager authManager,
|
IListFormAuthorizationManager authManager,
|
||||||
IListFormSelectAppService listFormSelectAppService,
|
IListFormSelectAppService listFormSelectAppService,
|
||||||
IQueryManager queryManager,
|
IQueryManager queryManager,
|
||||||
IdentityUserManager identityUserManager,
|
IdentityUserManager identityUserManager,
|
||||||
ISozsoftEmailSender erpEmailSender,
|
ISozsoftEmailSender erpEmailSender,
|
||||||
ISettingProvider settingProvider,
|
ISettingProvider settingProvider)
|
||||||
IStringLocalizer<PlatformResource> localizer)
|
|
||||||
{
|
{
|
||||||
this.criteriaRepository = criteriaRepository;
|
this.criteriaRepository = criteriaRepository;
|
||||||
this.noteRepository = noteRepository;
|
|
||||||
this.listFormManager = listFormManager;
|
this.listFormManager = listFormManager;
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.listFormSelectAppService = listFormSelectAppService;
|
this.listFormSelectAppService = listFormSelectAppService;
|
||||||
|
|
@ -62,7 +55,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
this.identityUserManager = identityUserManager;
|
this.identityUserManager = identityUserManager;
|
||||||
this.erpEmailSender = erpEmailSender;
|
this.erpEmailSender = erpEmailSender;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
this.localizer = localizer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("criteria")]
|
[HttpGet("criteria")]
|
||||||
|
|
@ -196,7 +188,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
criteria.ListFormCode = code;
|
criteria.ListFormCode = code;
|
||||||
criteria.Kind = NormalizeRequired(input.Kind, "Compare");
|
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.CompareColumn = NormalizeRequired(input.CompareColumn, "Price");
|
||||||
criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">");
|
criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">");
|
||||||
criteria.CompareValue = input.CompareValue;
|
criteria.CompareValue = input.CompareValue;
|
||||||
|
|
@ -308,14 +300,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
|
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
|
||||||
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
|
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
|
||||||
|
|
||||||
context.WorkflowNoteRows.Add(("Started By: ", ResolveCurrentUserDisplayName()));
|
return await RunUntilWaitAsync(context, start);
|
||||||
var result = await RunUntilWaitAsync(context, start);
|
|
||||||
await InsertWorkflowNoteAsync(
|
|
||||||
context,
|
|
||||||
$"Workflow Started: {start.Title}",
|
|
||||||
BuildWorkflowNoteContent(context.WorkflowNoteRows));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("decision")]
|
[HttpPost("decision")]
|
||||||
|
|
@ -380,15 +365,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
}
|
}
|
||||||
|
|
||||||
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
|
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
|
||||||
context.WorkflowNoteRows.Add(("Decision By: ", ResolveCurrentUserDisplayName()));
|
return await RunUntilWaitAsync(context, next);
|
||||||
AddWorkflowDecisionRows(context, current, input.Approved, input.Note ?? string.Empty);
|
|
||||||
var result = await RunUntilWaitAsync(context, next);
|
|
||||||
await InsertWorkflowNoteAsync(
|
|
||||||
context,
|
|
||||||
$"Workflow {(input.Approved ? "Approved" : "Rejected")}: {current.Title}",
|
|
||||||
BuildWorkflowNoteContent(context.WorkflowNoteRows));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<WorkflowRunResultDto> RunForEachKeyAsync(
|
private async Task<WorkflowRunResultDto> RunForEachKeyAsync(
|
||||||
|
|
@ -410,10 +387,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
CurrentNodeTitle = last?.CurrentNodeTitle,
|
CurrentNodeTitle = last?.CurrentNodeTitle,
|
||||||
CurrentNodeKind = last?.CurrentNodeKind,
|
CurrentNodeKind = last?.CurrentNodeKind,
|
||||||
WaitingApproval = results.Any(x => x.WaitingApproval),
|
WaitingApproval = results.Any(x => x.WaitingApproval),
|
||||||
Completed = results.All(x => x.Completed),
|
Completed = results.All(x => x.Completed)
|
||||||
ToastMessages = results
|
|
||||||
.SelectMany(result => result.ToastMessages ?? [])
|
|
||||||
.ToList()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,7 +419,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
.ToList();
|
.ToList();
|
||||||
var row = await GetRowAsync(code, listForm.KeyFieldName, keys[0]);
|
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)
|
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);
|
await UpdateRowAsync(context, update);
|
||||||
MergeRowValues(context.Row, update);
|
MergeRowValues(context.Row, update);
|
||||||
AddWorkflowNodeRows(context, node);
|
|
||||||
AddWorkflowToastMessage(context, node);
|
|
||||||
|
|
||||||
if (node.Kind == "Inform")
|
if (node.Kind == "Inform")
|
||||||
{
|
{
|
||||||
|
|
@ -561,7 +533,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
recipientEmail,
|
recipientEmail,
|
||||||
sender,
|
sender,
|
||||||
new { },
|
new { },
|
||||||
BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context)),
|
BuildInformEmailBody(context, node),
|
||||||
$"Workflow Bilgilendirme: {node.Title}",
|
$"Workflow Bilgilendirme: {node.Title}",
|
||||||
null,
|
null,
|
||||||
true);
|
true);
|
||||||
|
|
@ -570,7 +542,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ResolveApproverEmailAsync(string approver)
|
private async Task<string> ResolveApproverEmailAsync(string approver)
|
||||||
|
|
@ -594,257 +565,22 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return user.Email;
|
return user.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> BuildPreviousWorkflowNotesHtmlAsync(WorkflowRunContext context)
|
private static string BuildInformEmailBody(WorkflowRunContext context, ListFormWorkflow node)
|
||||||
{
|
|
||||||
var key = context.Keys?.FirstOrDefault()?.ToString();
|
|
||||||
if (key.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var notes = (await noteRepository.GetListAsync(note =>
|
|
||||||
note.EntityName == context.ListFormCode &&
|
|
||||||
note.EntityId == key &&
|
|
||||||
note.Type == "workflow"))
|
|
||||||
.OrderBy(note => note.CreationTime)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (notes.Count == 0)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var noteItems = notes.Select(note =>
|
|
||||||
$"""
|
|
||||||
<div style="margin: 0 0 12px 0; padding: 10px 12px; border: 1px solid #e5e7eb; border-radius: 6px;">
|
|
||||||
<div style="font-weight: 600; margin-bottom: 6px;">{Encode(note.Subject)}</div>
|
|
||||||
<div>{note.Content}</div>
|
|
||||||
</div>
|
|
||||||
""");
|
|
||||||
|
|
||||||
return string.Join(string.Empty, noteItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildInformEmailBody(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
ListFormWorkflow node,
|
|
||||||
string previousWorkflowNotesHtml)
|
|
||||||
{
|
{
|
||||||
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
||||||
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
||||||
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? 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 $"""
|
return $"""
|
||||||
<div style="font-family: Arial, sans-serif; color: #111827; line-height: 1.45;">
|
<p>Workflow bilgilendirme adimina ulasildi.</p>
|
||||||
<p>Workflow sürecinde bilgilendirme adımına ulaşıldı.</p>
|
<table>
|
||||||
<table style="border-collapse: collapse; width: 100%; margin-bottom: 16px;">
|
<tr><td><strong>Liste Formu</strong></td><td>{listFormCode}</td></tr>
|
||||||
<tr><td style="padding: 6px 8px;"><strong>Liste Formu</strong></td><td style="padding: 6px 8px;">{listFormCode}</td></tr>
|
<tr><td><strong>Adim</strong></td><td>{nodeTitle}</td></tr>
|
||||||
<tr><td style="padding: 6px 8px;"><strong>Kayıt</strong></td><td style="padding: 6px 8px;">{keyText}</td></tr>
|
<tr><td><strong>Kayit</strong></td><td>{keyText}</td></tr>
|
||||||
<tr><td style="padding: 6px 8px;"><strong>Bilgilendirme Adımı</strong></td><td style="padding: 6px 8px;">{nodeTitle}</td></tr>
|
</table>
|
||||||
<tr><td style="padding: 6px 8px;"><strong>Alıcı</strong></td><td style="padding: 6px 8px;">{recipient}</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3 style="margin: 18px 0 8px 0;">Bu İşlemdeki Süreç Özeti</h3>
|
|
||||||
{processRows}
|
|
||||||
|
|
||||||
{previousNotesSection}
|
|
||||||
</div>
|
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveCurrentUserDisplayName()
|
|
||||||
{
|
|
||||||
return CurrentUser.UserName
|
|
||||||
?? CurrentUser.Name
|
|
||||||
?? CurrentUser.Id?.ToString()
|
|
||||||
?? "System";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddWorkflowDecisionRows(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
ListFormWorkflow current,
|
|
||||||
bool approved,
|
|
||||||
string description)
|
|
||||||
{
|
|
||||||
var action = approved ? "Approved" : "Rejected";
|
|
||||||
context.WorkflowNoteRows.Add(("Decision: ", $"{action}: {FormatNode(current)}"));
|
|
||||||
context.WorkflowNoteRows.Add(("Description: ", description ?? string.Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddWorkflowNodeRows(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
ListFormWorkflow node)
|
|
||||||
{
|
|
||||||
var action = node.Kind switch
|
|
||||||
{
|
|
||||||
"Start" => "Started",
|
|
||||||
"Compare" => "Evaluated",
|
|
||||||
"Approval" => "Waiting Approval",
|
|
||||||
"Inform" => "Informed",
|
|
||||||
"End" => "Completed",
|
|
||||||
_ => "Processed"
|
|
||||||
};
|
|
||||||
|
|
||||||
context.WorkflowNoteRows.Add(($"{action}: ", $"{FormatNode(node)}"));
|
|
||||||
|
|
||||||
if (!node.Approver.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
context.WorkflowNoteRows.Add((node.Kind == "Inform" ? "Inform: " : "Approver: ", node.Approver));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddWorkflowToastMessage(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
ListFormWorkflow node)
|
|
||||||
{
|
|
||||||
var userName = ResolveCurrentUserDisplayName();
|
|
||||||
var next = FindNextToastNode(context, node);
|
|
||||||
var messageLines = node.Kind switch
|
|
||||||
{
|
|
||||||
"Start" => new[]
|
|
||||||
{
|
|
||||||
localizer["ListForms.ListForm.Workflow.WorkflowStarted"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
|
||||||
}.Concat(FormatNextToastNode(next)),
|
|
||||||
"Inform" => new[]
|
|
||||||
{
|
|
||||||
localizer["ListForms.ListForm.Workflow.InformReached"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
|
||||||
}.Concat(FormatNextToastNode(next)),
|
|
||||||
"End" => new[]
|
|
||||||
{
|
|
||||||
localizer["ListForms.ListForm.Workflow.WorkflowCompleted"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
|
||||||
},
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (messageLines != null)
|
|
||||||
{
|
|
||||||
context.ToastMessages.Add(string.Join(Environment.NewLine, messageLines));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListFormWorkflow FindNextToastNode(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
ListFormWorkflow node)
|
|
||||||
{
|
|
||||||
var current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, node));
|
|
||||||
var visited = new HashSet<string>();
|
|
||||||
|
|
||||||
while (current != null && visited.Add(current.Id))
|
|
||||||
{
|
|
||||||
if (current.Kind is "Approval" or "Inform" or "End")
|
|
||||||
{
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, current));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<string> FormatNextToastNode(ListFormWorkflow node)
|
|
||||||
{
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.Kind switch
|
|
||||||
{
|
|
||||||
"Approval" =>
|
|
||||||
[
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStep.Approval"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.ApproverUser", FormatToastUser(node.Approver)].Value
|
|
||||||
],
|
|
||||||
"Inform" =>
|
|
||||||
[
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStep.Inform"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
|
||||||
],
|
|
||||||
"End" =>
|
|
||||||
[
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStep.End"].Value,
|
|
||||||
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value
|
|
||||||
],
|
|
||||||
_ => [localizer["ListForms.ListForm.Workflow.NextStep", node.Title].Value]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatToastUser(string userName)
|
|
||||||
{
|
|
||||||
return userName.IsNullOrWhiteSpace()
|
|
||||||
? localizer["ListForms.ListForm.Workflow.UndefinedUser"].Value
|
|
||||||
: userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InsertWorkflowNoteAsync(
|
|
||||||
WorkflowRunContext context,
|
|
||||||
string subject,
|
|
||||||
string content)
|
|
||||||
{
|
|
||||||
var key = context.Keys?.FirstOrDefault();
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var note = new Note(GuidGenerator.Create())
|
|
||||||
{
|
|
||||||
TenantId = CurrentTenant.Id,
|
|
||||||
EntityName = context.ListFormCode,
|
|
||||||
EntityId = key.ToString(),
|
|
||||||
Type = "workflow",
|
|
||||||
Subject = subject,
|
|
||||||
Content = content,
|
|
||||||
FilesJson = "[]"
|
|
||||||
};
|
|
||||||
|
|
||||||
await noteRepository.InsertAsync(note, autoSave: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildWorkflowNoteContent(List<(string Label, string Value)> rows)
|
|
||||||
{
|
|
||||||
var tableRows = rows
|
|
||||||
.Where(row => !row.Value.IsNullOrWhiteSpace())
|
|
||||||
.Select(row =>
|
|
||||||
$"<tr><td><strong>{Encode(row.Label)}</strong></td><td>{Encode(row.Value)}</td></tr>");
|
|
||||||
|
|
||||||
return $"<table class=\"workflow-note-log\">{string.Join(string.Empty, tableRows)}</table>";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatNode(ListFormWorkflow node)
|
|
||||||
{
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var title = node.Title ?? string.Empty;
|
|
||||||
var kind = node.Kind ?? string.Empty;
|
|
||||||
return $"{title} ({kind} - {node.Id})";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Encode(string value)
|
|
||||||
{
|
|
||||||
return WebUtility.HtmlEncode(value ?? string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateRowAsync(WorkflowRunContext context, Dictionary<string, object> data)
|
private async Task UpdateRowAsync(WorkflowRunContext context, Dictionary<string, object> data)
|
||||||
{
|
{
|
||||||
await queryManager.GenerateAndRunQueryAsync<int>(
|
await queryManager.GenerateAndRunQueryAsync<int>(
|
||||||
|
|
@ -1026,8 +762,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
CurrentNodeTitle = node?.Title,
|
CurrentNodeTitle = node?.Title,
|
||||||
CurrentNodeKind = node?.Kind,
|
CurrentNodeKind = node?.Kind,
|
||||||
WaitingApproval = waitingApproval,
|
WaitingApproval = waitingApproval,
|
||||||
Completed = completed,
|
Completed = completed
|
||||||
ToastMessages = context.ToastMessages.ToList()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1171,36 +906,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return value.IsNullOrWhiteSpace() ? fallback : value.Trim();
|
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)
|
private static string SerializeCompareOutcomes(List<CompareOutcomeDto> outcomes)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(outcomes ?? []);
|
return JsonSerializer.Serialize(outcomes ?? []);
|
||||||
|
|
@ -1242,14 +947,12 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
private sealed record WorkflowRunContext(
|
private sealed record WorkflowRunContext(
|
||||||
string ListFormCode,
|
string ListFormCode,
|
||||||
|
string KeyFieldName,
|
||||||
object[] Keys,
|
object[] Keys,
|
||||||
WorkflowDto Workflow,
|
WorkflowDto Workflow,
|
||||||
List<ListFormWorkflow> Criteria,
|
List<ListFormWorkflow> Criteria,
|
||||||
IDictionary<string, object> Row)
|
IDictionary<string, object> Row)
|
||||||
{
|
{
|
||||||
public HashSet<string> UserUpdatedFields { get; } = [];
|
public HashSet<string> UserUpdatedFields { get; } = [];
|
||||||
public List<(string Label, string Value)> WorkflowNoteRows { get; } = [];
|
|
||||||
public List<string> ToastMessages { get; } = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,13 @@ public class MenuAppService : CrudAppService<
|
||||||
private readonly IRepository<LanguageText, Guid> _repositoryText;
|
private readonly IRepository<LanguageText, Guid> _repositoryText;
|
||||||
private readonly ITenantRepository _tenantRepository;
|
private readonly ITenantRepository _tenantRepository;
|
||||||
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
|
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
|
||||||
private readonly LanguageTextAppService _languageTextAppService;
|
|
||||||
|
|
||||||
public MenuAppService(
|
public MenuAppService(
|
||||||
IRepository<Menu, Guid> menuRepository,
|
IRepository<Menu, Guid> menuRepository,
|
||||||
IRepository<LanguageKey, Guid> languageKeyRepository,
|
IRepository<LanguageKey, Guid> languageKeyRepository,
|
||||||
IRepository<LanguageText, Guid> languageTextRepository,
|
IRepository<LanguageText, Guid> languageTextRepository,
|
||||||
ITenantRepository tenantRepository,
|
ITenantRepository tenantRepository,
|
||||||
IPermissionDefinitionRecordRepository permissionRepository,
|
IPermissionDefinitionRecordRepository permissionRepository
|
||||||
LanguageTextAppService languageTextAppService
|
|
||||||
) : base(menuRepository)
|
) : base(menuRepository)
|
||||||
{
|
{
|
||||||
_menuRepository = menuRepository;
|
_menuRepository = menuRepository;
|
||||||
|
|
@ -46,7 +44,6 @@ public class MenuAppService : CrudAppService<
|
||||||
_repositoryText = languageTextRepository;
|
_repositoryText = languageTextRepository;
|
||||||
_tenantRepository = tenantRepository;
|
_tenantRepository = tenantRepository;
|
||||||
_permissionRepository = permissionRepository;
|
_permissionRepository = permissionRepository;
|
||||||
_languageTextAppService = languageTextAppService;
|
|
||||||
|
|
||||||
CreatePolicyName = $"{AppCodes.Menus.Menu}.Create";
|
CreatePolicyName = $"{AppCodes.Menus.Menu}.Create";
|
||||||
UpdatePolicyName = $"{AppCodes.Menus.Menu}.Update";
|
UpdatePolicyName = $"{AppCodes.Menus.Menu}.Update";
|
||||||
|
|
@ -278,7 +275,7 @@ public class MenuAppService : CrudAppService<
|
||||||
if (existingEnText != null)
|
if (existingEnText != null)
|
||||||
{
|
{
|
||||||
existingEnText.Value = input.MenuTextEn;
|
existingEnText.Value = input.MenuTextEn;
|
||||||
await _repositoryText.UpdateAsync(existingEnText, autoSave: true);
|
await _repositoryText.UpdateAsync(existingEnText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -288,7 +285,7 @@ public class MenuAppService : CrudAppService<
|
||||||
CultureName = "en",
|
CultureName = "en",
|
||||||
Value = input.MenuTextEn,
|
Value = input.MenuTextEn,
|
||||||
ResourceName = PlatformConsts.AppName
|
ResourceName = PlatformConsts.AppName
|
||||||
}, autoSave: true);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Türkçe text oluşturuluyor veya güncelleniyor.
|
// Türkçe text oluşturuluyor veya güncelleniyor.
|
||||||
|
|
@ -300,7 +297,7 @@ public class MenuAppService : CrudAppService<
|
||||||
if (existingTrText != null)
|
if (existingTrText != null)
|
||||||
{
|
{
|
||||||
existingTrText.Value = input.MenuTextTr;
|
existingTrText.Value = input.MenuTextTr;
|
||||||
await _repositoryText.UpdateAsync(existingTrText, autoSave: true);
|
await _repositoryText.UpdateAsync(existingTrText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -310,12 +307,9 @@ public class MenuAppService : CrudAppService<
|
||||||
CultureName = "tr",
|
CultureName = "tr",
|
||||||
Value = input.MenuTextTr,
|
Value = input.MenuTextTr,
|
||||||
ResourceName = PlatformConsts.AppName
|
ResourceName = PlatformConsts.AppName
|
||||||
}, autoSave: true);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear Redis Cache
|
|
||||||
await _languageTextAppService.ClearRedisCacheAsync();
|
|
||||||
|
|
||||||
return await base.CreateAsync(input);
|
return await base.CreateAsync(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -674,7 +674,7 @@
|
||||||
"code": "Abp.Account.EnableLocalLogin",
|
"code": "Abp.Account.EnableLocalLogin",
|
||||||
"nameKey": "Abp.Account.EnableLocalLogin",
|
"nameKey": "Abp.Account.EnableLocalLogin",
|
||||||
"descriptionKey": "Abp.Account.EnableLocalLogin.Description",
|
"descriptionKey": "Abp.Account.EnableLocalLogin.Description",
|
||||||
"defaultValue": "True",
|
"defaultValue": "False",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "G|D",
|
"providers": "G|D",
|
||||||
"isInherited": false,
|
"isInherited": false,
|
||||||
|
|
@ -1071,22 +1071,6 @@
|
||||||
"dataType": "Number",
|
"dataType": "Number",
|
||||||
"selectOptions": {},
|
"selectOptions": {},
|
||||||
"order": 80
|
"order": 80
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
|
|
||||||
"nameKey": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount",
|
|
||||||
"descriptionKey": "Abp.Identity.OrganizationUnit.MaxUserMembershipCount.Description",
|
|
||||||
"defaultValue": "2147483647",
|
|
||||||
"isVisibleToClients": true,
|
|
||||||
"providers": "T|G|D",
|
|
||||||
"isInherited": true,
|
|
||||||
"isEncrypted": false,
|
|
||||||
"mainGroupKey": "Abp.Identity",
|
|
||||||
"subGroupKey": "Abp.Identity.OrganizationUnits",
|
|
||||||
"requiredPermissionName": "Abp.Identity.OrganizationUnits",
|
|
||||||
"dataType": "Number",
|
|
||||||
"selectOptions": {},
|
|
||||||
"order": 90
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"NotificationTypes": [],
|
"NotificationTypes": [],
|
||||||
|
|
|
||||||
|
|
@ -3642,36 +3642,6 @@
|
||||||
"en": "The record was deleted",
|
"en": "The record was deleted",
|
||||||
"tr": "Kayıt silindi"
|
"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",
|
"resourceName": "Platform",
|
||||||
"key": "KayitEklendi",
|
"key": "KayitEklendi",
|
||||||
|
|
@ -3765,8 +3735,8 @@
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.ListForm.AddNewRecord",
|
"key": "ListForms.ListForm.AddNewRecord",
|
||||||
"en": "Add",
|
"en": "Add New Record",
|
||||||
"tr": "Ekle"
|
"tr": "Yeni Kayıt Ekle"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
|
|
@ -4184,9 +4154,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.ListForm.NoteModal.Type.Workflow",
|
"key": "ListForms.ListForm.NoteModal.Type.Activity",
|
||||||
"en": "Workflow",
|
"en": "Activity",
|
||||||
"tr": "Akış"
|
"tr": "Aktivite"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
|
|
@ -16766,24 +16736,6 @@
|
||||||
"en": "Approver",
|
"en": "Approver",
|
||||||
"tr": "Onayla"
|
"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",
|
"resourceName": "Platform",
|
||||||
"key": "App.Listform.ListformField.NextOnStart",
|
"key": "App.Listform.ListformField.NextOnStart",
|
||||||
|
|
@ -17522,12 +17474,6 @@
|
||||||
"en": "Columns load after selecting a Select Command",
|
"en": "Columns load after selecting a Select Command",
|
||||||
"tr": "Select Command seçince sütunlar yüklenir"
|
"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",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.Wizard.Step3.GenerateFromTable",
|
"key": "ListForms.Wizard.Step3.GenerateFromTable",
|
||||||
|
|
@ -17768,12 +17714,6 @@
|
||||||
"en": "Key Field Type",
|
"en": "Key Field Type",
|
||||||
"tr": "Anahtar Alan Tipi"
|
"tr": "Anahtar Alan Tipi"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.Wizard.Step4.ColumnsAndFormLayout",
|
|
||||||
"en": "Columns & Form Layout",
|
|
||||||
"tr": "Sütunlar ve Form Yerleşimi"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.Wizard.Step4.SelectedColumns",
|
"key": "ListForms.Wizard.Step4.SelectedColumns",
|
||||||
|
|
@ -17798,24 +17738,6 @@
|
||||||
"en": "Field",
|
"en": "Field",
|
||||||
"tr": "Alan"
|
"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",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.Wizard.Step4.DeployAndSave",
|
"key": "ListForms.Wizard.Step4.DeployAndSave",
|
||||||
|
|
@ -18116,12 +18038,6 @@
|
||||||
"en": "Add Multi-Tenant Column",
|
"en": "Add Multi-Tenant Column",
|
||||||
"tr": "MultiTenant Sütunları Ekle"
|
"tr": "MultiTenant Sütunları Ekle"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "App.SqlQueryManager.AddWorkflowColumns",
|
|
||||||
"en": "Add Workflow Column",
|
|
||||||
"tr": "Workflow Sütunları Ekle"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.SqlQueryManager.ClearAllColumns",
|
"key": "App.SqlQueryManager.ClearAllColumns",
|
||||||
|
|
@ -19070,12 +18986,6 @@
|
||||||
"en": "Approval Status Field Name",
|
"en": "Approval Status Field Name",
|
||||||
"tr": "Onay Durumu Alanı Adı"
|
"tr": "Onay Durumu Alanı Adı"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListFormEdit.Workflow.IsFilterUserName",
|
|
||||||
"en": "Filter User Name?",
|
|
||||||
"tr": "Kullanıcı Adı Filtresin mi?"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.ListFormEdit.Workflow.ApprovalDescriptionFieldName",
|
"key": "ListForms.ListFormEdit.Workflow.ApprovalDescriptionFieldName",
|
||||||
|
|
@ -19321,84 +19231,6 @@
|
||||||
"key": "FileManager.SortByModifiedDesc",
|
"key": "FileManager.SortByModifiedDesc",
|
||||||
"en": "Modified (Newest)",
|
"en": "Modified (Newest)",
|
||||||
"tr": "Değiştirilme (En Yeni)"
|
"tr": "Değiştirilme (En Yeni)"
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.WorkflowStarted",
|
|
||||||
"en": "Operation: Workflow started",
|
|
||||||
"tr": "İşlem: Workflow başlatıldı"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.InformReached",
|
|
||||||
"en": "Operation: Workflow inform step reached",
|
|
||||||
"tr": "İşlem: Workflow bilgilendirme adımına ulaştı"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.WorkflowCompleted",
|
|
||||||
"en": "Operation: Workflow completed",
|
|
||||||
"tr": "İşlem: Workflow tamamlandı"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.Step",
|
|
||||||
"en": "Step: {0}",
|
|
||||||
"tr": "Adım: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.PerformedBy",
|
|
||||||
"en": "Performed by: {0}",
|
|
||||||
"tr": "İşlemi yapan: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.InformUser",
|
|
||||||
"en": "User to inform: {0}",
|
|
||||||
"tr": "Bilgilendirilecek kullanıcı: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.ApproverUser",
|
|
||||||
"en": "Approver user: {0}",
|
|
||||||
"tr": "Onaylayacak kullanıcı: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.NextStep.Approval",
|
|
||||||
"en": "Next step: Approval",
|
|
||||||
"tr": "Sonraki adım: Onay"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.NextStep.Inform",
|
|
||||||
"en": "Next step: Inform",
|
|
||||||
"tr": "Sonraki adım: Bilgilendirme"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.NextStep.End",
|
|
||||||
"en": "Next step: Workflow end",
|
|
||||||
"tr": "Sonraki adım: Workflow bitiş"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.NextStepName",
|
|
||||||
"en": "Next step name: {0}",
|
|
||||||
"tr": "Sonraki adım adı: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.NextStep",
|
|
||||||
"en": "Next step: {0}",
|
|
||||||
"tr": "Sonraki adım: {0}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "ListForms.ListForm.Workflow.UndefinedUser",
|
|
||||||
"en": "Undefined",
|
|
||||||
"tr": "Tanımsız"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -797,7 +797,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
PermissionJson = DefaultPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, listFormName, PlatformConsts.IdentityPermissions.Users.Update, PlatformConsts.IdentityPermissions.Users.Delete, PlatformConsts.IdentityPermissions.Users.Export, PlatformConsts.IdentityPermissions.Users.Import, PlatformConsts.IdentityPermissions.Users.Note),
|
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",
|
DeleteCommand = $"UPDATE \"AbpUsers\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 710, true, true, true, true, false),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 730, true, true, true, true, false),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||||
new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[
|
new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[
|
||||||
new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public static class ListFormSeeder_DefaultJsons
|
||||||
{
|
{
|
||||||
public static string DefaultDeleteCommand(string tableName)
|
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[]
|
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid, string newId = "@NEWID") => JsonSerializer.Serialize(new FieldsDefaultValue[]
|
||||||
|
|
|
||||||
|
|
@ -2302,7 +2302,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
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>() {
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||||
new() {
|
new() {
|
||||||
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=[
|
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=[
|
||||||
|
|
@ -4025,7 +4025,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
HeaderFilterJson = DefaultHeaderFilterJson,
|
HeaderFilterJson = DefaultHeaderFilterJson,
|
||||||
SearchPanelJson = DefaultSearchPanelJson,
|
SearchPanelJson = DefaultSearchPanelJson,
|
||||||
GroupPanelJson = DefaultGroupPanelJson,
|
GroupPanelJson = DefaultGroupPanelJson,
|
||||||
SelectionJson = DefaultSelectionSingleJson,
|
SelectionJson = DefaultSelectionMultipleJson,
|
||||||
ColumnOptionJson = DefaultColumnOptionJson(false),
|
ColumnOptionJson = DefaultColumnOptionJson(false),
|
||||||
PermissionJson = DefaultPermissionJson(listFormName),
|
PermissionJson = DefaultPermissionJson(listFormName),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -36,7 +36,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
||||||
private readonly IRepository<ListForm, Guid> _repoListForm;
|
private readonly IRepository<ListForm, Guid> _repoListForm;
|
||||||
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
||||||
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
|
|
||||||
private readonly ILogger<WizardDataSeeder> _logger;
|
private readonly ILogger<WizardDataSeeder> _logger;
|
||||||
|
|
||||||
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
||||||
|
|
@ -52,7 +51,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
IRepository<DataSource, Guid> repoDataSource,
|
IRepository<DataSource, Guid> repoDataSource,
|
||||||
IRepository<ListForm, Guid> repoListForm,
|
IRepository<ListForm, Guid> repoListForm,
|
||||||
IRepository<ListFormField, Guid> repoListFormField,
|
IRepository<ListFormField, Guid> repoListFormField,
|
||||||
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
|
|
||||||
ILogger<WizardDataSeeder> logger)
|
ILogger<WizardDataSeeder> logger)
|
||||||
{
|
{
|
||||||
_repoLangKey = repoLangKey;
|
_repoLangKey = repoLangKey;
|
||||||
|
|
@ -64,7 +62,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
_repoDataSource = repoDataSource;
|
_repoDataSource = repoDataSource;
|
||||||
_repoListForm = repoListForm;
|
_repoListForm = repoListForm;
|
||||||
_repoListFormField = repoListFormField;
|
_repoListFormField = repoListFormField;
|
||||||
_repoListFormWorkflow = repoListFormWorkflow;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,29 +134,13 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
input.Widgets ??= new List<WidgetEditDto>();
|
input.Widgets ??= new List<WidgetEditDto>();
|
||||||
input.Workflow ??= new WorkflowDto();
|
input.Workflow ??= new WorkflowDto();
|
||||||
input.WorkflowCriteria ??= new List<ListFormWorkflowCriteriaDto>();
|
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;
|
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
|
||||||
|
|
||||||
var wizardName = input.WizardName.Trim();
|
var wizardName = input.WizardName.Trim();
|
||||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
var titleLangKey = WizardConsts.WizardKeyTitle(wizardName);
|
||||||
? WizardConsts.WizardKey(wizardName)
|
var nameLangKey = WizardConsts.WizardKey(wizardName);
|
||||||
: input.MenuCode.Trim();
|
var descLangKey = WizardConsts.WizardKeyDesc(wizardName);
|
||||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
var code = WizardConsts.WizardKey(wizardName);
|
||||||
? code
|
|
||||||
: input.ListFormCode.Trim();
|
|
||||||
var titleLangKey = $"{listFormCode}.Title";
|
|
||||||
var nameLangKey = code;
|
|
||||||
var descLangKey = $"{listFormCode}.Desc";
|
|
||||||
var permCreateName = $"{code}.Create";
|
|
||||||
var permUpdateName = $"{code}.Update";
|
|
||||||
var permDeleteName = $"{code}.Delete";
|
|
||||||
var permExportName = $"{code}.Export";
|
|
||||||
var permImportName = $"{code}.Import";
|
|
||||||
var permNoteName = $"{code}.Note";
|
|
||||||
|
|
||||||
// Dil - Language Keys
|
// Dil - Language Keys
|
||||||
await CreateLangKeyAsync(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
|
await CreateLangKeyAsync(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
|
||||||
|
|
@ -172,10 +153,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
{
|
{
|
||||||
await _repoPermGroup.InsertAsync(
|
await _repoPermGroup.InsertAsync(
|
||||||
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
|
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
|
||||||
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
|
await CreateLangKeyAsync(groupName, groupName, groupName);
|
||||||
await EnsureLangKeyAsync(groupName);
|
|
||||||
else
|
|
||||||
await CreateLangKeyAsync(groupName, groupName, groupName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
|
// 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(
|
permRead = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
||||||
Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: true);
|
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)
|
if (permCreate == null)
|
||||||
permCreate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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)
|
if (permUpdate == null)
|
||||||
permUpdate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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)
|
if (permDelete == null)
|
||||||
permDelete = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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)
|
if (permExport == null)
|
||||||
permExport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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)
|
if (permImport == null)
|
||||||
permImport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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)
|
if (permNote == null)
|
||||||
permNote = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
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
|
// // Permission Grants - Admin role için, sadece eksik olanları ekle
|
||||||
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
|
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
|
||||||
|
|
@ -241,18 +219,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
if (menuParent == null)
|
if (menuParent == null)
|
||||||
{
|
{
|
||||||
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
||||||
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
|
await CreateLangKeyAsync(WizardConsts.WizardKeyParent(wizardName), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
|
||||||
? input.MenuParentIcon
|
|
||||||
: !string.IsNullOrWhiteSpace(input.MenuIcon)
|
|
||||||
? input.MenuIcon
|
|
||||||
: WizardConsts.MenuIcon;
|
|
||||||
await CreateLangKeyAsync(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
|
|
||||||
menuParent = await _repoMenu.InsertAsync(new Menu
|
menuParent = await _repoMenu.InsertAsync(new Menu
|
||||||
{
|
{
|
||||||
Code = input.MenuParentCode,
|
Code = input.MenuParentCode,
|
||||||
DisplayName = input.MenuParentCode,
|
DisplayName = WizardConsts.WizardKeyParent(wizardName),
|
||||||
IsDisabled = false,
|
IsDisabled = false,
|
||||||
Icon = menuParentIcon,
|
|
||||||
Order = maxRootOrder + 1,
|
Order = maxRootOrder + 1,
|
||||||
}, autoSave: true);
|
}, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +276,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
ColSpan = g.ColCount,
|
ColSpan = g.ColCount,
|
||||||
ItemType = "group",
|
ItemType = "group",
|
||||||
Items = g.Items
|
Items = g.Items
|
||||||
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
|
.Where(i => i.DataField != input.KeyFieldName)
|
||||||
.Select((it, ii) => new EditingFormItemDto
|
.Select((it, ii) => new EditingFormItemDto
|
||||||
{
|
{
|
||||||
Order = ii + 1,
|
Order = ii + 1,
|
||||||
|
|
@ -317,7 +289,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
})
|
})
|
||||||
.ToArray()
|
.ToArray()
|
||||||
})
|
})
|
||||||
.Where(g => g.Items.Length > 0)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// ListForm - varsa sil, yeniden ekle
|
// ListForm - varsa sil, yeniden ekle
|
||||||
|
|
@ -333,12 +304,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
|
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
|
// ListForm
|
||||||
await _repoListForm.InsertAsync(new ListForm
|
await _repoListForm.InsertAsync(new ListForm
|
||||||
{
|
{
|
||||||
|
|
@ -346,7 +311,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
PageSize = 10,
|
PageSize = 10,
|
||||||
ExportJson = WizardConsts.DefaultExportJson,
|
ExportJson = WizardConsts.DefaultExportJson,
|
||||||
IsSubForm = false,
|
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),
|
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler),
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
ListFormCode = input.ListFormCode,
|
ListFormCode = input.ListFormCode,
|
||||||
|
|
@ -367,7 +332,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
||||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
||||||
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
||||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
|
SelectionJson = WizardConsts.DefaultSelectionSingleJson,
|
||||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
||||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
||||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
||||||
|
|
@ -416,34 +381,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
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)
|
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)
|
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(key)) return;
|
if (string.IsNullOrWhiteSpace(key)) return;
|
||||||
|
|
@ -544,16 +427,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
}, autoSave: true);
|
}, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -773,10 +773,10 @@ public static class PlatformConsts
|
||||||
{
|
{
|
||||||
public static class ParameterTypes
|
public static class ParameterTypes
|
||||||
{
|
{
|
||||||
public const string Static = "Static";
|
public const string Static = "S";
|
||||||
public const string Query = "Query";
|
public const string Query = "Q";
|
||||||
public const string Path = "Path";
|
public const string Path = "P";
|
||||||
public const string Body = "Body";
|
public const string Body = "B";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,9 @@ public static class WizardConsts
|
||||||
public static readonly string DefaultSearchPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
public static readonly string DefaultSearchPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
||||||
public static readonly string DefaultGroupPanelJson = 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
|
AllowSelectAll = false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ public static class WizardConsts
|
||||||
R = permissionName,
|
R = permissionName,
|
||||||
U = permissionName + ".Update",
|
U = permissionName + ".Update",
|
||||||
E = true,
|
E = true,
|
||||||
I = true,
|
I = false,
|
||||||
Deny = false
|
Deny = false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +163,7 @@ public static class WizardConsts
|
||||||
|
|
||||||
public static string DefaultDeleteCommand(string tableName)
|
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)
|
public static string DefaultInsertFieldsDefaultValueJson(DbType dbType = DbType.Guid)
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,6 @@ namespace Sozsoft.Platform.Entities;
|
||||||
|
|
||||||
public class Note : FullAuditedEntity<Guid>, IMultiTenant
|
public class Note : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
{
|
{
|
||||||
public Note()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Note(Guid id) : base(id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid? TenantId { get; set; }
|
public Guid? TenantId { get; set; }
|
||||||
public string EntityName { get; set; }
|
public string EntityName { get; set; }
|
||||||
public string EntityId { get; set; }
|
public string EntityId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,7 @@ public class DefaultValueManager : PlatformDomainService, IDefaultValueManager
|
||||||
else if (defaultField.Value == PlatformConsts.DefaultValues.Year)
|
else if (defaultField.Value == PlatformConsts.DefaultValues.Year)
|
||||||
value = Clock.Now.Year;
|
value = Clock.Now.Year;
|
||||||
else if (defaultField.Value == PlatformConsts.DefaultValues.Id)
|
else if (defaultField.Value == PlatformConsts.DefaultValues.Id)
|
||||||
value = op == OperationEnum.Delete
|
value = keys?.FirstOrDefault();
|
||||||
? keys
|
|
||||||
: keys?.FirstOrDefault();
|
|
||||||
else if (defaultField.Value == PlatformConsts.DefaultValues.NewId)
|
else if (defaultField.Value == PlatformConsts.DefaultValues.NewId)
|
||||||
value = Guid.NewGuid();
|
value = Guid.NewGuid();
|
||||||
else if (defaultField.Value == PlatformConsts.DefaultValues.Selected_Ids)
|
else if (defaultField.Value == PlatformConsts.DefaultValues.Selected_Ids)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
|
|
@ -170,7 +169,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
// oncelik Command alanindadir, dolu ise silme islemi buradaki sorguya yonlendirilir
|
// oncelik Command alanindadir, dolu ise silme islemi buradaki sorguya yonlendirilir
|
||||||
if (!string.IsNullOrEmpty(command))
|
if (!string.IsNullOrEmpty(command))
|
||||||
{
|
{
|
||||||
sql = NormalizeCollectionParameterCommand(command, parameters, dataSourceType);
|
sql = command;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -190,7 +189,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
{
|
{
|
||||||
var where = dataSourceType switch
|
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})",
|
DataSourceTypeEnum.Postgresql => $"\"{listForm.KeyFieldName}\" = ANY(@{listForm.KeyFieldName})",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
@ -210,14 +209,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
string where = string.Empty;
|
string where = string.Empty;
|
||||||
if (parameters.Any())
|
if (parameters.Any())
|
||||||
{
|
{
|
||||||
where = string.Join(
|
where = string.Join(" AND ", parameters.Select(a => $"\"{a.Key}\" IN (@{a.Key})").ToList());
|
||||||
" AND ",
|
|
||||||
parameters.Select(a => dataSourceType switch
|
|
||||||
{
|
|
||||||
DataSourceTypeEnum.Mssql => $"\"{a.Key}\" IN @{a.Key}",
|
|
||||||
DataSourceTypeEnum.Postgresql => $"\"{a.Key}\" = ANY(@{a.Key})",
|
|
||||||
_ => "1 = 0",
|
|
||||||
}).ToList());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -228,7 +220,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
|
|
||||||
where = dataSourceType switch
|
where = dataSourceType switch
|
||||||
{
|
{
|
||||||
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN @{listForm.KeyFieldName}",
|
DataSourceTypeEnum.Mssql => $"\"{listForm.KeyFieldName}\" IN (@{listForm.KeyFieldName})",
|
||||||
DataSourceTypeEnum.Postgresql => $"\"{listForm.KeyFieldName}\" = ANY(@{listForm.KeyFieldName})",
|
DataSourceTypeEnum.Postgresql => $"\"{listForm.KeyFieldName}\" = ANY(@{listForm.KeyFieldName})",
|
||||||
_ => "1 = 0",
|
_ => "1 = 0",
|
||||||
};
|
};
|
||||||
|
|
@ -239,78 +231,5 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string NormalizeCollectionParameterCommand(
|
|
||||||
string command,
|
|
||||||
Dictionary<string, object> parameters,
|
|
||||||
DataSourceTypeEnum dataSourceType)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(command) || parameters == null || parameters.Count == 0)
|
|
||||||
{
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sql = command;
|
|
||||||
foreach (var parameter in parameters)
|
|
||||||
{
|
|
||||||
if (!IsCollectionParameter(parameter.Value))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var escapedParameterName = Regex.Escape(parameter.Key);
|
|
||||||
sql = dataSourceType switch
|
|
||||||
{
|
|
||||||
DataSourceTypeEnum.Mssql => NormalizeMssqlCollectionParameter(sql, escapedParameterName, parameter.Key),
|
|
||||||
DataSourceTypeEnum.Postgresql => NormalizePostgresqlCollectionParameter(sql, escapedParameterName, parameter.Key),
|
|
||||||
_ => sql
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCollectionParameter(object value)
|
|
||||||
{
|
|
||||||
return value is System.Collections.IEnumerable
|
|
||||||
&& value is not string
|
|
||||||
&& value is not byte[];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeMssqlCollectionParameter(
|
|
||||||
string sql,
|
|
||||||
string escapedParameterName,
|
|
||||||
string parameterName)
|
|
||||||
{
|
|
||||||
sql = Regex.Replace(
|
|
||||||
sql,
|
|
||||||
$@"IN\s*\(\s*@{escapedParameterName}\s*\)",
|
|
||||||
$"IN @{parameterName}",
|
|
||||||
RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
return Regex.Replace(
|
|
||||||
sql,
|
|
||||||
$@"(?<column>(""[^""]+""|\[[^\]]+\]|`[^`]+`|\w+))\s*=\s*@{escapedParameterName}\b",
|
|
||||||
match => $"{match.Groups["column"].Value} IN @{parameterName}",
|
|
||||||
RegexOptions.IgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizePostgresqlCollectionParameter(
|
|
||||||
string sql,
|
|
||||||
string escapedParameterName,
|
|
||||||
string parameterName)
|
|
||||||
{
|
|
||||||
sql = Regex.Replace(
|
|
||||||
sql,
|
|
||||||
$@"IN\s*\(\s*@{escapedParameterName}\s*\)",
|
|
||||||
$"= ANY(@{parameterName})",
|
|
||||||
RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
return Regex.Replace(
|
|
||||||
sql,
|
|
||||||
$@"(?<column>(""[^""]+""|\[[^\]]+\]|`[^`]+`|\w+))\s*=\s*@{escapedParameterName}\b",
|
|
||||||
match => $"{match.Groups["column"].Value} = ANY(@{parameterName})",
|
|
||||||
RegexOptions.IgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,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())
|
if (!whereParts.Any())
|
||||||
{
|
{
|
||||||
whereParts.Add("1 = 1");
|
whereParts.Add("1 = 1");
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -2,12 +2,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Sozsoft.Platform;
|
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
using Volo.Abp.DependencyInjection;
|
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)
|
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 dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, 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)
|
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 dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, 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)
|
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 dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, 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)
|
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 dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, 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)
|
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 dbConnection = await GetOrCreateConnectionAsync(cs);
|
||||||
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
var transaction = await GetOrCreateTransactionAsync(dbConnection, cs);
|
||||||
|
|
||||||
return await dbConnection.ExecuteAsync(sql, param, transaction);
|
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 ------------------
|
// ------------------ Dispose ------------------
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,6 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.PositionX).IsRequired();
|
b.Property(x => x.PositionX).IsRequired();
|
||||||
b.Property(x => x.PositionY).IsRequired();
|
b.Property(x => x.PositionY).IsRequired();
|
||||||
b.Property(x => x.CompareOutcomesJson).HasColumnType("text");
|
b.Property(x => x.CompareOutcomesJson).HasColumnType("text");
|
||||||
b.HasIndex(x => new { x.ListFormCode, x.Title }).IsUnique();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<Note>(b =>
|
builder.Entity<Note>(b =>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260606212623_Initial")]
|
[Migration("20260602070242_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -3486,9 +3486,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("ListFormCode", "Title")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3905,12 +3905,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
table: "Sas_H_ListFormImportLog",
|
table: "Sas_H_ListFormImportLog",
|
||||||
column: "ImportId");
|
column: "ImportId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Sas_H_ListFormWorkflow_ListFormCode_Title",
|
|
||||||
table: "Sas_H_ListFormWorkflow",
|
|
||||||
columns: new[] { "ListFormCode", "Title" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Sas_H_Menu_Code",
|
name: "IX_Sas_H_Menu_Code",
|
||||||
table: "Sas_H_Menu",
|
table: "Sas_H_Menu",
|
||||||
|
|
@ -3483,9 +3483,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("ListFormCode", "Title")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
b.ToTable("Sas_H_ListFormWorkflow", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -324,16 +324,16 @@
|
||||||
"Url": "/dil/",
|
"Url": "/dil/",
|
||||||
"Method": "GET",
|
"Method": "GET",
|
||||||
"DataSourceCode": "Default",
|
"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": [
|
"ParametersJson": [
|
||||||
{
|
{
|
||||||
"Type": "Path",
|
"Type": "P",
|
||||||
"Name": "CultureName",
|
"Name": "CultureName",
|
||||||
"DefaultValue": "ar",
|
"DefaultValue": "ar",
|
||||||
"Path": "/dil/:CultureName/"
|
"Path": "/dil/:CultureName/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": "Static",
|
"Type": "S",
|
||||||
"Name": "IsEnabled",
|
"Name": "IsEnabled",
|
||||||
"DefaultValue": "true"
|
"DefaultValue": "true"
|
||||||
}
|
}
|
||||||
|
|
@ -1456,36 +1456,6 @@
|
||||||
"DepartmentName": "Muhasebe",
|
"DepartmentName": "Muhasebe",
|
||||||
"Name": "Muhasebe Şefi",
|
"Name": "Muhasebe Şefi",
|
||||||
"ParentName": "Muhasebe Müdürü"
|
"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": [
|
"Announcements": [
|
||||||
|
|
|
||||||
|
|
@ -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
|
# sozsoft.com
|
||||||
server {
|
server {
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ volumes:
|
||||||
rocket_mongodb_data:
|
rocket_mongodb_data:
|
||||||
driver: local
|
driver: local
|
||||||
n8n_data:
|
n8n_data:
|
||||||
netdataconfig:
|
|
||||||
netdatalib:
|
|
||||||
netdatacache:
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
forgejo:
|
forgejo:
|
||||||
|
|
@ -111,30 +108,3 @@ services:
|
||||||
- /etc/ssl/sozsoft.com:/etc/ssl/sozsoft.com:ro # Sertifikaları mount ettik
|
- /etc/ssl/sozsoft.com:/etc/ssl/sozsoft.com:ro # Sertifikaları mount ettik
|
||||||
- ./logs/coturn:/var/log # Logları dışarı al (opsiyonel)
|
- ./logs/coturn:/var/log # Logları dışarı al (opsiyonel)
|
||||||
command: ["turnserver", "-c", "/etc/coturn/turnserver.conf"]
|
command: ["turnserver", "-c", "/etc/coturn/turnserver.conf"]
|
||||||
|
|
||||||
dashboard:
|
|
||||||
image: netdata/netdata:stable
|
|
||||||
container_name: dashboard
|
|
||||||
hostname: kursserver
|
|
||||||
restart: unless-stopped
|
|
||||||
pid: host
|
|
||||||
network_mode: host
|
|
||||||
cap_add:
|
|
||||||
- SYS_PTRACE
|
|
||||||
- SYS_ADMIN
|
|
||||||
security_opt:
|
|
||||||
- apparmor:unconfined
|
|
||||||
volumes:
|
|
||||||
- netdataconfig:/etc/netdata
|
|
||||||
- netdatalib:/var/lib/netdata
|
|
||||||
- netdatacache:/var/cache/netdata
|
|
||||||
- /:/host/root:ro,rslave
|
|
||||||
- /etc/passwd:/host/etc/passwd:ro
|
|
||||||
- /etc/group:/host/etc/group:ro
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
- /proc:/host/proc:ro
|
|
||||||
- /sys:/host/sys:ro
|
|
||||||
- /etc/os-release:/host/etc/os-release:ro
|
|
||||||
- /var/log:/host/var/log:ro
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- /run/dbus:/run/dbus:ro
|
|
||||||
|
|
@ -20,7 +20,6 @@ SUBDOMAINS=(
|
||||||
"sozsoft.com"
|
"sozsoft.com"
|
||||||
"www.sozsoft.com"
|
"www.sozsoft.com"
|
||||||
"demo.sozsoft.com"
|
"demo.sozsoft.com"
|
||||||
"dashboard.sozsoft.com"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
echo "Subdomain'ler için SSL sertifikaları alınıyor..."
|
echo "Subdomain'ler için SSL sertifikaları alınıyor..."
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,6 @@
|
||||||
{
|
{
|
||||||
"commit": "dc293fc",
|
"commit": "0d4703c",
|
||||||
"releases": [
|
"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",
|
"version": "1.1.03",
|
||||||
"buildDate": "2026-05-30",
|
"buildDate": "2026-05-30",
|
||||||
|
|
@ -150,4 +137,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ export interface ListFormWizardColumnItemDto {
|
||||||
editorScript: string
|
editorScript: string
|
||||||
colSpan: number
|
colSpan: number
|
||||||
isRequired: boolean
|
isRequired: boolean
|
||||||
includeInEditingForm: boolean
|
|
||||||
dbSourceType: number
|
dbSourceType: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +54,6 @@ export interface ListFormWizardDto {
|
||||||
languageTextMenuParentTr: string
|
languageTextMenuParentTr: string
|
||||||
permissionGroupName: string
|
permissionGroupName: string
|
||||||
menuParentCode: string
|
menuParentCode: string
|
||||||
menuParentIcon?: string
|
|
||||||
menuIcon: string
|
menuIcon: string
|
||||||
dataSourceCode: string
|
dataSourceCode: string
|
||||||
dataSourceConnectionString: string
|
dataSourceConnectionString: string
|
||||||
|
|
@ -115,7 +113,6 @@ export interface WizardSeedFileItemDto {
|
||||||
editorScript: string
|
editorScript: string
|
||||||
colSpan: number
|
colSpan: number
|
||||||
isRequired: boolean
|
isRequired: boolean
|
||||||
includeInEditingForm?: boolean
|
|
||||||
dbSourceType: number
|
dbSourceType: number
|
||||||
turkishCaption?: string
|
turkishCaption?: string
|
||||||
englishCaption?: string
|
englishCaption?: string
|
||||||
|
|
|
||||||
|
|
@ -908,7 +908,6 @@ export interface WidgetEditDto {
|
||||||
|
|
||||||
export interface WorkflowDto {
|
export interface WorkflowDto {
|
||||||
approvalUserFieldName: string
|
approvalUserFieldName: string
|
||||||
isFilterUserName: boolean
|
|
||||||
approvalDateFieldName: string
|
approvalDateFieldName: string
|
||||||
approvalStatusFieldName: string
|
approvalStatusFieldName: string
|
||||||
approvalDescriptionFieldName: string
|
approvalDescriptionFieldName: string
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -11,11 +11,6 @@ import { ListResultDto, PagedAndSortedResultRequestDto, PagedResultDto } from '.
|
||||||
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
||||||
import apiService from './api.service'
|
import apiService from './api.service'
|
||||||
|
|
||||||
export interface UserAvatarUpdateInput {
|
|
||||||
userId: string
|
|
||||||
avatar?: File
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
||||||
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
@ -41,21 +36,6 @@ export const putUserDetail = (input: UserInfoViewModel) =>
|
||||||
data: input,
|
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) =>
|
export const putUserLookout = (input: UserInfoViewModel) =>
|
||||||
apiService.fetchData({
|
apiService.fetchData({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ export interface WorkflowRunResultDto {
|
||||||
currentNodeKind?: string | null
|
currentNodeKind?: string | null
|
||||||
waitingApproval: boolean
|
waitingApproval: boolean
|
||||||
completed: boolean
|
completed: boolean
|
||||||
toastMessages?: string[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {
|
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm {
|
||||||
const sharedPerson = item.approver || ''
|
const sharedPerson = item.approver || ''
|
||||||
|
|
||||||
|
|
@ -432,8 +347,7 @@ export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput {
|
export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput {
|
||||||
const sharedPerson =
|
const sharedPerson = item.approver || ''
|
||||||
item.kind === 'Approval' || item.kind === 'Inform' ? item.approver || '' : ''
|
|
||||||
const compareOutcomes = (item.compareOutcomes || [])
|
const compareOutcomes = (item.compareOutcomes || [])
|
||||||
.slice(0, 4)
|
.slice(0, 4)
|
||||||
.filter((outcome) => outcome.label?.trim())
|
.filter((outcome) => outcome.label?.trim())
|
||||||
|
|
@ -590,7 +504,7 @@ export function criteriaSummary(item: WorkflowCriteriaDto) {
|
||||||
if (item.kind === 'Approval' || item.kind === 'Inform') {
|
if (item.kind === 'Approval' || item.kind === 'Inform') {
|
||||||
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
|
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) {
|
export function targetTitle(criteria: WorkflowCriteriaDto[], id?: string | null) {
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,15 @@ import {
|
||||||
emptyCriteria,
|
emptyCriteria,
|
||||||
normalizeCriteria,
|
normalizeCriteria,
|
||||||
toCriteriaForm,
|
toCriteriaForm,
|
||||||
uniqueCriteriaTitle,
|
|
||||||
type WorkflowCriteriaForm,
|
type WorkflowCriteriaForm,
|
||||||
} from '@/utils/workflow/workflowHelpers'
|
} from '@/utils/workflow/workflowHelpers'
|
||||||
import { workflowService, type WorkflowCriteriaDto } from '@/services/workflow.service'
|
import { workflowService, type WorkflowCriteriaDto } from '@/services/workflow.service'
|
||||||
import { WorkflowDesigner } from '../workflow/WorkflowDesigner'
|
import { WorkflowDesigner } from '../workflow/WorkflowDesigner'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
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 { ListFormEditTabs } from '@/proxy/admin/list-form/options'
|
||||||
import { bool, object, string } from 'yup'
|
import { object, string } from 'yup'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { FormEditProps } from './FormEdit'
|
import { FormEditProps } from './FormEdit'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -113,17 +112,9 @@ export function FormTabWorkflow(
|
||||||
const saveCriteria = (event: FormEvent<HTMLFormElement>) => {
|
const saveCriteria = (event: FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
runAction(async () => {
|
runAction(async () => {
|
||||||
const normalized = normalizeCriteria(criteriaForm)
|
|
||||||
const preferredTitle = criteriaForm.title || normalized.title
|
|
||||||
await workflowService.saveCriteria({
|
await workflowService.saveCriteria({
|
||||||
...normalized,
|
...normalizeCriteria(criteriaForm),
|
||||||
listFormCode: props.listFormCode,
|
listFormCode: props.listFormCode,
|
||||||
title: uniqueCriteriaTitle(
|
|
||||||
normalized.kind || '',
|
|
||||||
currentCriteria,
|
|
||||||
normalized.id,
|
|
||||||
preferredTitle,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
setSelectedId('')
|
setSelectedId('')
|
||||||
})
|
})
|
||||||
|
|
@ -132,11 +123,9 @@ export function FormTabWorkflow(
|
||||||
const addCriteria = (kind: string) => {
|
const addCriteria = (kind: string) => {
|
||||||
setDesignerTab('flow')
|
setDesignerTab('flow')
|
||||||
runAction(async () => {
|
runAction(async () => {
|
||||||
const nextTitle = uniqueCriteriaTitle(kind, currentCriteria)
|
|
||||||
const saved = await workflowService.saveCriteria({
|
const saved = await workflowService.saveCriteria({
|
||||||
...normalizeCriteria(emptyCriteria(kind, props.listFormCode)),
|
...normalizeCriteria(emptyCriteria(kind, props.listFormCode)),
|
||||||
listFormCode: props.listFormCode,
|
listFormCode: props.listFormCode,
|
||||||
title: nextTitle,
|
|
||||||
positionX: 80 + (currentCriteria.length % 5) * 230,
|
positionX: 80 + (currentCriteria.length % 5) * 230,
|
||||||
positionY: 220 + Math.floor(currentCriteria.length / 5) * 140,
|
positionY: 220 + Math.floor(currentCriteria.length / 5) * 140,
|
||||||
})
|
})
|
||||||
|
|
@ -307,13 +296,10 @@ export function FormTabWorkflow(
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = object().shape({
|
const schema = object().shape({
|
||||||
workflowDto: object().shape({
|
approvalUserFieldName: string().required(),
|
||||||
approvalUserFieldName: string().required(),
|
approvalStatusFieldName: string().required(),
|
||||||
isFilterUserName: bool(),
|
approvalDateFieldName: string(),
|
||||||
approvalStatusFieldName: string().required(),
|
approvalDescriptionFieldName: string(),
|
||||||
approvalDateFieldName: string(),
|
|
||||||
approvalDescriptionFieldName: string(),
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialValues = useStoreState((s) => s.admin.lists.values)
|
const initialValues = useStoreState((s) => s.admin.lists.values)
|
||||||
|
|
@ -334,7 +320,7 @@ export function FormTabWorkflow(
|
||||||
<Form>
|
<Form>
|
||||||
<FormContainer size="sm">
|
<FormContainer size="sm">
|
||||||
<Card className="my-2">
|
<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
|
<FormItem
|
||||||
asterisk
|
asterisk
|
||||||
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalUserFieldName')}
|
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalUserFieldName')}
|
||||||
|
|
@ -384,7 +370,7 @@ export function FormTabWorkflow(
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalDateFieldName')}
|
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalDateFieldName')}
|
||||||
invalid={
|
invalid={
|
||||||
|
|
@ -435,22 +421,9 @@ export function FormTabWorkflow(
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</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>
|
</div>
|
||||||
|
|
||||||
<Button block variant="solid" type="submit" loading={isSubmitting}>
|
<Button block variant="solid" loading={isSubmitting}>
|
||||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Form, Formik, FormikProps } from 'formik'
|
import { Form, Formik, FormikProps } from 'formik'
|
||||||
import type { KeyboardEvent } from 'react'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
@ -71,7 +70,6 @@ const initialValues: ListFormWizardDto = {
|
||||||
languageTextMenuParentTr: '',
|
languageTextMenuParentTr: '',
|
||||||
permissionGroupName: '',
|
permissionGroupName: '',
|
||||||
menuParentCode: '',
|
menuParentCode: '',
|
||||||
menuParentIcon: '',
|
|
||||||
menuIcon: '',
|
menuIcon: '',
|
||||||
dataSourceCode: '',
|
dataSourceCode: '',
|
||||||
dataSourceConnectionString: '',
|
dataSourceConnectionString: '',
|
||||||
|
|
@ -96,7 +94,6 @@ const initialValues: ListFormWizardDto = {
|
||||||
widgets: [],
|
widgets: [],
|
||||||
workflow: {
|
workflow: {
|
||||||
approvalUserFieldName: '',
|
approvalUserFieldName: '',
|
||||||
isFilterUserName: false,
|
|
||||||
approvalDateFieldName: '',
|
approvalDateFieldName: '',
|
||||||
approvalStatusFieldName: '',
|
approvalStatusFieldName: '',
|
||||||
approvalDescriptionFieldName: '',
|
approvalDescriptionFieldName: '',
|
||||||
|
|
@ -224,7 +221,6 @@ const Wizard = () => {
|
||||||
const [widgets, setWidgets] = useState<WidgetEditDto[]>([])
|
const [widgets, setWidgets] = useState<WidgetEditDto[]>([])
|
||||||
const [workflow, setWorkflow] = useState<WorkflowDto>({
|
const [workflow, setWorkflow] = useState<WorkflowDto>({
|
||||||
approvalUserFieldName: '',
|
approvalUserFieldName: '',
|
||||||
isFilterUserName: false,
|
|
||||||
approvalDateFieldName: '',
|
approvalDateFieldName: '',
|
||||||
approvalStatusFieldName: '',
|
approvalStatusFieldName: '',
|
||||||
approvalDescriptionFieldName: '',
|
approvalDescriptionFieldName: '',
|
||||||
|
|
@ -244,18 +240,6 @@ const Wizard = () => {
|
||||||
])
|
])
|
||||||
|
|
||||||
const isAuditColumn = (columnName: string) => AUDIT_COLUMNS.has(columnName.toLowerCase())
|
const isAuditColumn = (columnName: string) => AUDIT_COLUMNS.has(columnName.toLowerCase())
|
||||||
const isTenantColumn = (columnName: string) => columnName.toLowerCase() === 'tenantid'
|
|
||||||
const isAutoSelectedColumn = (columnName: string, isTenant = false) =>
|
|
||||||
!isAuditColumn(columnName) && !(isTenant && isTenantColumn(columnName))
|
|
||||||
|
|
||||||
const removeTenantColumn = (columns: Set<string>) =>
|
|
||||||
new Set([...columns].filter((columnName) => !isTenantColumn(columnName)))
|
|
||||||
|
|
||||||
const removeTenantGroupItems = (groups: WizardGroup[]) =>
|
|
||||||
groups.map((group) => ({
|
|
||||||
...group,
|
|
||||||
items: group.items.filter((item) => !isTenantColumn(item.dataField)),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const loadColumns = async (dsCode: string, schema: string, name: string) => {
|
const loadColumns = async (dsCode: string, schema: string, name: string) => {
|
||||||
if (!dsCode || !name) {
|
if (!dsCode || !name) {
|
||||||
|
|
@ -269,13 +253,12 @@ const Wizard = () => {
|
||||||
const res = await sqlObjectManagerService.getTableColumns(dsCode, schema, name)
|
const res = await sqlObjectManagerService.getTableColumns(dsCode, schema, name)
|
||||||
const cols = res.data ?? []
|
const cols = res.data ?? []
|
||||||
setSelectCommandColumns(cols)
|
setSelectCommandColumns(cols)
|
||||||
const colNames = new Set(cols.map((c) => c.columnName.toLowerCase()))
|
const selectableColumns = cols.filter((c) => !isAuditColumn(c.columnName))
|
||||||
const hasTenantColumn = colNames.has('tenantid')
|
|
||||||
const selectableColumns = cols.filter((c) => isAutoSelectedColumn(c.columnName, hasTenantColumn))
|
|
||||||
setSelectedColumns(new Set(selectableColumns.map((c) => c.columnName)))
|
setSelectedColumns(new Set(selectableColumns.map((c) => c.columnName)))
|
||||||
setEditingGroups([])
|
setEditingGroups([])
|
||||||
// Auto-check isTenant / isBranch based on column presence
|
// Auto-check isTenant / isBranch based on column presence
|
||||||
formikRef.current?.setFieldValue('isTenant', hasTenantColumn)
|
const colNames = new Set(cols.map((c) => c.columnName.toLowerCase()))
|
||||||
|
formikRef.current?.setFieldValue('isTenant', colNames.has('tenantid'))
|
||||||
formikRef.current?.setFieldValue('isBranch', colNames.has('branchid'))
|
formikRef.current?.setFieldValue('isBranch', colNames.has('branchid'))
|
||||||
// Auto-select first column as key field
|
// Auto-select first column as key field
|
||||||
if (cols.length > 0) {
|
if (cols.length > 0) {
|
||||||
|
|
@ -301,23 +284,17 @@ const Wizard = () => {
|
||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleAllColumns = (all: boolean, isTenant = formikRef.current?.values.isTenant ?? false) =>
|
const toggleAllColumns = (all: boolean) =>
|
||||||
setSelectedColumns(
|
setSelectedColumns(
|
||||||
all
|
all
|
||||||
? new Set(
|
? new Set(
|
||||||
selectCommandColumns
|
selectCommandColumns
|
||||||
.filter((c) => isAutoSelectedColumn(c.columnName, isTenant))
|
.filter((c) => !isAuditColumn(c.columnName))
|
||||||
.map((c) => c.columnName),
|
.map((c) => c.columnName),
|
||||||
)
|
)
|
||||||
: new Set(),
|
: new Set(),
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleTenantChange = (isTenant: boolean) => {
|
|
||||||
if (!isTenant) return
|
|
||||||
setSelectedColumns((prev) => removeTenantColumn(prev))
|
|
||||||
setEditingGroups((prev) => removeTenantGroupItems(prev))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDataSourceList = async () => {
|
const getDataSourceList = async () => {
|
||||||
setIsLoadingDataSource(true)
|
setIsLoadingDataSource(true)
|
||||||
const response = await getDataSources()
|
const response = await getDataSources()
|
||||||
|
|
@ -414,7 +391,6 @@ const Wizard = () => {
|
||||||
languageTextMenuParentTr: w.languageTextMenuParentTr ?? '',
|
languageTextMenuParentTr: w.languageTextMenuParentTr ?? '',
|
||||||
permissionGroupName: w.permissionGroupName ?? '',
|
permissionGroupName: w.permissionGroupName ?? '',
|
||||||
menuParentCode: w.menuParentCode ?? '',
|
menuParentCode: w.menuParentCode ?? '',
|
||||||
menuParentIcon: w.menuParentIcon ?? '',
|
|
||||||
menuIcon: w.menuIcon ?? '',
|
menuIcon: w.menuIcon ?? '',
|
||||||
dataSourceCode: w.dataSourceCode ?? '',
|
dataSourceCode: w.dataSourceCode ?? '',
|
||||||
dataSourceConnectionString: w.dataSourceConnectionString ?? '',
|
dataSourceConnectionString: w.dataSourceConnectionString ?? '',
|
||||||
|
|
@ -439,7 +415,6 @@ const Wizard = () => {
|
||||||
widgets: w.widgets ?? [],
|
widgets: w.widgets ?? [],
|
||||||
workflow: w.workflow ?? {
|
workflow: w.workflow ?? {
|
||||||
approvalUserFieldName: '',
|
approvalUserFieldName: '',
|
||||||
isFilterUserName: false,
|
|
||||||
approvalDateFieldName: '',
|
approvalDateFieldName: '',
|
||||||
approvalStatusFieldName: '',
|
approvalStatusFieldName: '',
|
||||||
approvalDescriptionFieldName: '',
|
approvalDescriptionFieldName: '',
|
||||||
|
|
@ -478,7 +453,6 @@ const Wizard = () => {
|
||||||
editorScript: it.editorScript ?? '',
|
editorScript: it.editorScript ?? '',
|
||||||
colSpan: it.colSpan ?? 1,
|
colSpan: it.colSpan ?? 1,
|
||||||
isRequired: it.isRequired ?? false,
|
isRequired: it.isRequired ?? false,
|
||||||
includeInEditingForm: it.includeInEditingForm ?? true,
|
|
||||||
turkishCaption: it.turkishCaption ?? it.dataField,
|
turkishCaption: it.turkishCaption ?? it.dataField,
|
||||||
englishCaption: it.englishCaption ?? it.dataField,
|
englishCaption: it.englishCaption ?? it.dataField,
|
||||||
captionName: it.captionName ?? `App.Listform.ListformField.${it.dataField}`,
|
captionName: it.captionName ?? `App.Listform.ListformField.${it.dataField}`,
|
||||||
|
|
@ -503,7 +477,6 @@ const Wizard = () => {
|
||||||
setWorkflow(
|
setWorkflow(
|
||||||
w.workflow ?? {
|
w.workflow ?? {
|
||||||
approvalUserFieldName: '',
|
approvalUserFieldName: '',
|
||||||
isFilterUserName: false,
|
|
||||||
approvalDateFieldName: '',
|
approvalDateFieldName: '',
|
||||||
approvalStatusFieldName: '',
|
approvalStatusFieldName: '',
|
||||||
approvalDescriptionFieldName: '',
|
approvalDescriptionFieldName: '',
|
||||||
|
|
@ -543,38 +516,24 @@ const Wizard = () => {
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
const handleWizardNameChange = (name: string) => {
|
const handleWizardNameChange = (name: string) => {
|
||||||
const formik = formikRef.current
|
|
||||||
const spacedLabel = toSpacedLabel(name)
|
const spacedLabel = toSpacedLabel(name)
|
||||||
const previousSpacedLabel = toSpacedLabel(formik?.values.wizardName ?? '')
|
|
||||||
const derived = deriveListFormCode(name)
|
const derived = deriveListFormCode(name)
|
||||||
|
|
||||||
const setAutoText = (field: keyof Pick<
|
formikRef.current?.setFieldValue('wizardName', name)
|
||||||
ListFormWizardDto,
|
formikRef.current?.setFieldValue('listFormCode', derived)
|
||||||
| 'languageTextMenuEn'
|
formikRef.current?.setFieldValue('menuCode', derived)
|
||||||
| 'languageTextMenuTr'
|
formikRef.current?.setFieldValue('languageTextMenuEn', spacedLabel)
|
||||||
| 'languageTextTitleEn'
|
formikRef.current?.setFieldValue('languageTextMenuTr', spacedLabel)
|
||||||
| 'languageTextTitleTr'
|
formikRef.current?.setFieldValue('languageTextTitleEn', spacedLabel)
|
||||||
| 'languageTextDescEn'
|
formikRef.current?.setFieldValue('languageTextTitleTr', spacedLabel)
|
||||||
| 'languageTextDescTr'
|
formikRef.current?.setFieldValue('languageTextDescEn', spacedLabel)
|
||||||
>) => {
|
formikRef.current?.setFieldValue('languageTextDescTr', spacedLabel)
|
||||||
const current = formik?.values[field]
|
|
||||||
if (!current || current === previousSpacedLabel) {
|
|
||||||
formik?.setFieldValue(field, spacedLabel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
formik?.setFieldValue('wizardName', name)
|
|
||||||
formik?.setFieldValue('listFormCode', derived)
|
|
||||||
formik?.setFieldValue('menuCode', derived)
|
|
||||||
setAutoText('languageTextMenuEn')
|
|
||||||
setAutoText('languageTextMenuTr')
|
|
||||||
setAutoText('languageTextTitleEn')
|
|
||||||
setAutoText('languageTextTitleTr')
|
|
||||||
setAutoText('languageTextDescEn')
|
|
||||||
setAutoText('languageTextDescTr')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const applyPermissionGroupFromRoot = (rootCode: string) => {
|
const handleMenuParentChange = (code: string) => {
|
||||||
|
formikRef.current?.setFieldValue('menuParentCode', code)
|
||||||
|
if (!code) return
|
||||||
|
const rootCode = findRootCode(rawMenuItems, code)
|
||||||
const rootItem = rawMenuItems.find((i) => i.code === rootCode)
|
const rootItem = rawMenuItems.find((i) => i.code === rootCode)
|
||||||
|
|
||||||
// 1. Use group field if set
|
// 1. Use group field if set
|
||||||
|
|
@ -606,32 +565,6 @@ const Wizard = () => {
|
||||||
formikRef.current?.setFieldValue('permissionGroupName', rootCode)
|
formikRef.current?.setFieldValue('permissionGroupName', rootCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMenuParentChange = (code: string) => {
|
|
||||||
formikRef.current?.setFieldValue('menuParentCode', code)
|
|
||||||
const selectedMenu = rawMenuItems.find((item) => item.code === code)
|
|
||||||
formikRef.current?.setFieldValue('menuParentIcon', selectedMenu?.icon ?? '')
|
|
||||||
if (!code) return
|
|
||||||
applyPermissionGroupFromRoot(findRootCode(rawMenuItems, code))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMenuCreated = async (menu: {
|
|
||||||
code: string
|
|
||||||
parentCode?: string
|
|
||||||
menuTextEn: string
|
|
||||||
menuTextTr: string
|
|
||||||
icon?: string
|
|
||||||
}) => {
|
|
||||||
formikRef.current?.setFieldValue('menuParentCode', menu.code)
|
|
||||||
formikRef.current?.setFieldValue('menuParentIcon', menu.icon ?? '')
|
|
||||||
formikRef.current?.setFieldValue('languageTextMenuParentEn', menu.menuTextEn)
|
|
||||||
formikRef.current?.setFieldValue('languageTextMenuParentTr', menu.menuTextTr)
|
|
||||||
|
|
||||||
const rootCode = menu.parentCode ? findRootCode(rawMenuItems, menu.parentCode) : menu.code
|
|
||||||
applyPermissionGroupFromRoot(rootCode)
|
|
||||||
|
|
||||||
await getMenuList()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleNext = async () => {
|
const handleNext = async () => {
|
||||||
if (!formikRef.current) return
|
if (!formikRef.current) return
|
||||||
const errors = await formikRef.current.validateForm()
|
const errors = await formikRef.current.validateForm()
|
||||||
|
|
@ -653,19 +586,6 @@ const Wizard = () => {
|
||||||
const handleBack = () => setCurrentStep(0)
|
const handleBack = () => setCurrentStep(0)
|
||||||
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
||||||
|
|
||||||
const preventEnterSubmit = (event: KeyboardEvent<HTMLFormElement>) => {
|
|
||||||
if (event.key !== 'Enter') return
|
|
||||||
|
|
||||||
const target = event.target as HTMLElement
|
|
||||||
const tagName = target.tagName.toLowerCase()
|
|
||||||
const isTextArea = tagName === 'textarea'
|
|
||||||
const isExplicitSubmit = target.getAttribute('type') === 'submit'
|
|
||||||
|
|
||||||
if (!isTextArea && !isExplicitSubmit) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleNext2 = async () => {
|
const handleNext2 = async () => {
|
||||||
if (!formikRef.current) return
|
if (!formikRef.current) return
|
||||||
const errors = await formikRef.current.validateForm()
|
const errors = await formikRef.current.validateForm()
|
||||||
|
|
@ -707,7 +627,6 @@ const Wizard = () => {
|
||||||
editorScript: item.editorScript ?? '',
|
editorScript: item.editorScript ?? '',
|
||||||
colSpan: item.colSpan,
|
colSpan: item.colSpan,
|
||||||
isRequired: item.isRequired,
|
isRequired: item.isRequired,
|
||||||
includeInEditingForm: item.includeInEditingForm,
|
|
||||||
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12,
|
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12,
|
||||||
turkishCaption: item.turkishCaption,
|
turkishCaption: item.turkishCaption,
|
||||||
englishCaption: item.englishCaption,
|
englishCaption: item.englishCaption,
|
||||||
|
|
@ -757,10 +676,7 @@ const Wizard = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mb-6 mt-2">
|
<div className="mb-6 mt-2">
|
||||||
<Steps
|
<Steps current={currentStep}>
|
||||||
current={currentStep}
|
|
||||||
className="flex flex-row flex-wrap !justify-start gap-y-2 lg:flex-nowrap lg:!justify-between"
|
|
||||||
>
|
|
||||||
<Steps.Item title={translate('::ListForms.Wizard.MenuInfo') || 'Menu Info'} />
|
<Steps.Item title={translate('::ListForms.Wizard.MenuInfo') || 'Menu Info'} />
|
||||||
<Steps.Item
|
<Steps.Item
|
||||||
title={translate('::ListForms.Wizard.ListFormSettings') || 'List Form Settings'}
|
title={translate('::ListForms.Wizard.ListFormSettings') || 'List Form Settings'}
|
||||||
|
|
@ -785,12 +701,40 @@ const Wizard = () => {
|
||||||
innerRef={formikRef}
|
innerRef={formikRef}
|
||||||
initialValues={{ ...initialValues }}
|
initialValues={{ ...initialValues }}
|
||||||
validationSchema={listFormValidationSchema}
|
validationSchema={listFormValidationSchema}
|
||||||
onSubmit={(_, { setSubmitting }) => {
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
setSubmitting(false)
|
setSubmitting(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 🔴 1. Kaydet (bekle)
|
||||||
|
await postListFormWizard({ ...values })
|
||||||
|
|
||||||
|
// 🔴 2. Config güncelle (bekle)
|
||||||
|
await getConfig(true)
|
||||||
|
|
||||||
|
// 🔴 3. Navigate
|
||||||
|
navigate(
|
||||||
|
ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode),
|
||||||
|
{ replace: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
// 🔴 4. Toast (istersen navigate öncesi de olabilir)
|
||||||
|
toast.push(
|
||||||
|
<Notification type="success" duration={2000}>
|
||||||
|
{translate('::ListForms.FormBilgileriKaydedildi')}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.push(<Notification title={error.message} type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ touched, errors, isSubmitting, values }) => (
|
{({ touched, errors, isSubmitting, values }) => (
|
||||||
<Form onKeyDown={preventEnterSubmit}>
|
<Form>
|
||||||
<FormContainer size={currentStep >= 2 ? undefined : 'sm'}>
|
<FormContainer size={currentStep >= 2 ? undefined : 'sm'}>
|
||||||
{/* ─── Step 1: Basic Info ─────────────────────────────── */}
|
{/* ─── Step 1: Basic Info ─────────────────────────────── */}
|
||||||
{currentStep === 0 && (
|
{currentStep === 0 && (
|
||||||
|
|
@ -804,11 +748,7 @@ const Wizard = () => {
|
||||||
menuTree={menuTree}
|
menuTree={menuTree}
|
||||||
isLoadingMenu={isLoadingMenu}
|
isLoadingMenu={isLoadingMenu}
|
||||||
onMenuParentChange={handleMenuParentChange}
|
onMenuParentChange={handleMenuParentChange}
|
||||||
onClearMenuParent={() => {
|
onClearMenuParent={() => formikRef.current?.setFieldValue('menuParentCode', '')}
|
||||||
formikRef.current?.setFieldValue('menuParentCode', '')
|
|
||||||
formikRef.current?.setFieldValue('menuParentIcon', '')
|
|
||||||
}}
|
|
||||||
onMenuCreated={handleMenuCreated}
|
|
||||||
onReloadMenu={getMenuList}
|
onReloadMenu={getMenuList}
|
||||||
permissionGroupList={permissionGroupList}
|
permissionGroupList={permissionGroupList}
|
||||||
isLoadingPermissionGroup={isLoadingPermissionGroup}
|
isLoadingPermissionGroup={isLoadingPermissionGroup}
|
||||||
|
|
@ -830,7 +770,6 @@ const Wizard = () => {
|
||||||
onDataSourceNewChange={setIsDataSourceNew}
|
onDataSourceNewChange={setIsDataSourceNew}
|
||||||
dbObjects={dbObjects}
|
dbObjects={dbObjects}
|
||||||
isLoadingDbObjects={isLoadingDbObjects}
|
isLoadingDbObjects={isLoadingDbObjects}
|
||||||
onDbObjectsRefresh={loadDbObjects}
|
|
||||||
selectCommandColumns={selectCommandColumns}
|
selectCommandColumns={selectCommandColumns}
|
||||||
isLoadingColumns={isLoadingColumns}
|
isLoadingColumns={isLoadingColumns}
|
||||||
selectedColumns={selectedColumns}
|
selectedColumns={selectedColumns}
|
||||||
|
|
@ -840,8 +779,7 @@ const Wizard = () => {
|
||||||
setSelectedColumns(new Set())
|
setSelectedColumns(new Set())
|
||||||
}}
|
}}
|
||||||
onToggleColumn={toggleColumn}
|
onToggleColumn={toggleColumn}
|
||||||
onToggleAllColumns={(all) => toggleAllColumns(all, values.isTenant)}
|
onToggleAllColumns={toggleAllColumns}
|
||||||
onTenantChange={handleTenantChange}
|
|
||||||
translate={translate}
|
translate={translate}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onNext={handleNext2}
|
onNext={handleNext2}
|
||||||
|
|
|
||||||
|
|
@ -414,14 +414,6 @@ export interface WizardStep1Props {
|
||||||
isLoadingMenu: boolean
|
isLoadingMenu: boolean
|
||||||
onMenuParentChange: (code: string) => void
|
onMenuParentChange: (code: string) => void
|
||||||
onClearMenuParent: () => void
|
onClearMenuParent: () => void
|
||||||
onMenuCreated: (menu: {
|
|
||||||
code: string
|
|
||||||
parentCode?: string
|
|
||||||
menuTextEn: string
|
|
||||||
menuTextTr: string
|
|
||||||
icon?: string
|
|
||||||
shortName?: string
|
|
||||||
}) => void | Promise<void>
|
|
||||||
onReloadMenu: () => void
|
onReloadMenu: () => void
|
||||||
permissionGroupList: SelectBoxOption[]
|
permissionGroupList: SelectBoxOption[]
|
||||||
isLoadingPermissionGroup: boolean
|
isLoadingPermissionGroup: boolean
|
||||||
|
|
@ -440,7 +432,6 @@ const WizardStep1 = ({
|
||||||
isLoadingMenu,
|
isLoadingMenu,
|
||||||
onMenuParentChange,
|
onMenuParentChange,
|
||||||
onClearMenuParent,
|
onClearMenuParent,
|
||||||
onMenuCreated,
|
|
||||||
onReloadMenu,
|
onReloadMenu,
|
||||||
permissionGroupList,
|
permissionGroupList,
|
||||||
isLoadingPermissionGroup,
|
isLoadingPermissionGroup,
|
||||||
|
|
@ -553,7 +544,7 @@ const WizardStep1 = ({
|
||||||
initialParentCode={menuDialogParentCode}
|
initialParentCode={menuDialogParentCode}
|
||||||
initialOrder={menuDialogInitialOrder}
|
initialOrder={menuDialogInitialOrder}
|
||||||
rawItems={rawMenuItems}
|
rawItems={rawMenuItems}
|
||||||
onSaved={onMenuCreated}
|
onSaved={onReloadMenu}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,11 @@ import { Button, Checkbox, FormItem, Input, Select } from '@/components/ui'
|
||||||
import { SelectCommandTypeEnum } from '@/proxy/form/models'
|
import { SelectCommandTypeEnum } from '@/proxy/form/models'
|
||||||
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { Field, FieldProps, FormikErrors, FormikTouched, useFormikContext } from 'formik'
|
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||||
import { useState } from 'react'
|
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { FaArrowLeft, FaArrowRight, FaPlus } from 'react-icons/fa'
|
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
|
||||||
import { dbSourceTypeOptions, listFormDefaultLayoutOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from '../edit/options'
|
import { dbSourceTypeOptions, listFormDefaultLayoutOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from '../edit/options'
|
||||||
import { ListFormWizardDto } from '@/proxy/admin/wizard/models'
|
import { ListFormWizardDto } from '@/proxy/admin/wizard/models'
|
||||||
import SqlTableDesignerDialog from '@/views/developerKit/SqlTableDesignerDialog'
|
|
||||||
|
|
||||||
// ─── Props ────────────────────────────────────────────────────────────────────
|
// ─── Props ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -25,7 +23,6 @@ export interface WizardStep2Props {
|
||||||
// DB Objects
|
// DB Objects
|
||||||
dbObjects: SqlObjectExplorerDto | null
|
dbObjects: SqlObjectExplorerDto | null
|
||||||
isLoadingDbObjects: boolean
|
isLoadingDbObjects: boolean
|
||||||
onDbObjectsRefresh: (dsCode: string) => void | Promise<void>
|
|
||||||
// Columns
|
// Columns
|
||||||
selectCommandColumns: DatabaseColumnDto[]
|
selectCommandColumns: DatabaseColumnDto[]
|
||||||
isLoadingColumns: boolean
|
isLoadingColumns: boolean
|
||||||
|
|
@ -34,7 +31,6 @@ export interface WizardStep2Props {
|
||||||
onClearColumns: () => void
|
onClearColumns: () => void
|
||||||
onToggleColumn: (col: string) => void
|
onToggleColumn: (col: string) => void
|
||||||
onToggleAllColumns: (all: boolean) => void
|
onToggleAllColumns: (all: boolean) => void
|
||||||
onTenantChange: (isTenant: boolean) => void
|
|
||||||
// Navigation
|
// Navigation
|
||||||
translate: (key: string) => string
|
translate: (key: string) => string
|
||||||
onBack: () => void
|
onBack: () => void
|
||||||
|
|
@ -54,7 +50,6 @@ const WizardStep2 = ({
|
||||||
onDataSourceNewChange,
|
onDataSourceNewChange,
|
||||||
dbObjects,
|
dbObjects,
|
||||||
isLoadingDbObjects,
|
isLoadingDbObjects,
|
||||||
onDbObjectsRefresh,
|
|
||||||
selectCommandColumns,
|
selectCommandColumns,
|
||||||
isLoadingColumns,
|
isLoadingColumns,
|
||||||
selectedColumns,
|
selectedColumns,
|
||||||
|
|
@ -62,23 +57,10 @@ const WizardStep2 = ({
|
||||||
onClearColumns,
|
onClearColumns,
|
||||||
onToggleColumn,
|
onToggleColumn,
|
||||||
onToggleAllColumns,
|
onToggleAllColumns,
|
||||||
onTenantChange,
|
|
||||||
translate,
|
translate,
|
||||||
onBack,
|
onBack,
|
||||||
onNext,
|
onNext,
|
||||||
}: WizardStep2Props) => {
|
}: WizardStep2Props) => {
|
||||||
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
|
|
||||||
const formik = useFormikContext<ListFormWizardDto>()
|
|
||||||
|
|
||||||
const handleTableDeployed = async (table: { schemaName: string; tableName: string }) => {
|
|
||||||
await onDbObjectsRefresh(values.dataSourceCode)
|
|
||||||
formik.setFieldValue('selectCommand', table.tableName)
|
|
||||||
formik.setFieldValue('selectCommandType', SelectCommandTypeEnum.Table)
|
|
||||||
formik.setFieldValue('keyFieldName', '')
|
|
||||||
formik.setFieldTouched('keyFieldName', false)
|
|
||||||
onLoadColumns(values.dataSourceCode, table.schemaName || 'dbo', table.tableName)
|
|
||||||
}
|
|
||||||
|
|
||||||
const step2Missing = [
|
const step2Missing = [
|
||||||
!values.listFormCode && translate('::App.Listform.ListformField.ListFormCode'),
|
!values.listFormCode && translate('::App.Listform.ListformField.ListFormCode'),
|
||||||
!values.dataSourceCode && translate('::ListForms.Wizard.Step4.DataSource'),
|
!values.dataSourceCode && translate('::ListForms.Wizard.Step4.DataSource'),
|
||||||
|
|
@ -235,9 +217,7 @@ const WizardStep2 = ({
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<Select
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<Select
|
|
||||||
field={field}
|
field={field}
|
||||||
form={form}
|
form={form}
|
||||||
isClearable
|
isClearable
|
||||||
|
|
@ -276,18 +256,7 @@ const WizardStep2 = ({
|
||||||
form.setFieldTouched('keyFieldName', false)
|
form.setFieldTouched('keyFieldName', false)
|
||||||
onClearColumns()
|
onClearColumns()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="solid"
|
|
||||||
icon={<FaPlus />}
|
|
||||||
disabled={!values.dataSourceCode}
|
|
||||||
onClick={() => setShowTableDesignerDialog(true)}
|
|
||||||
>
|
|
||||||
New Table
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
@ -359,18 +328,7 @@ const WizardStep2 = ({
|
||||||
invalid={!!(errors.isTenant && touched.isTenant)}
|
invalid={!!(errors.isTenant && touched.isTenant)}
|
||||||
errorMessage={errors.isTenant}
|
errorMessage={errors.isTenant}
|
||||||
>
|
>
|
||||||
<Field name="isTenant">
|
<Field type="checkbox" autoComplete="off" name="isTenant" component={Checkbox} />
|
||||||
{({ field, form }: FieldProps<boolean>) => (
|
|
||||||
<Checkbox
|
|
||||||
name={field.name}
|
|
||||||
checked={Boolean(field.value)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
form.setFieldValue(field.name, checked)
|
|
||||||
onTenantChange(checked)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
|
|
@ -742,7 +700,6 @@ const WizardStep2 = ({
|
||||||
<div className="flex items-center gap-2 ml-3">
|
<div className="flex items-center gap-2 ml-3">
|
||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
type="button"
|
|
||||||
onClick={() => onToggleAllColumns(true)}
|
onClick={() => onToggleAllColumns(true)}
|
||||||
className="text-xs px-2 py-0.5 rounded bg-indigo-500 text-white hover:bg-indigo-600"
|
className="text-xs px-2 py-0.5 rounded bg-indigo-500 text-white hover:bg-indigo-600"
|
||||||
>
|
>
|
||||||
|
|
@ -750,7 +707,6 @@ const WizardStep2 = ({
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
type="button"
|
|
||||||
onClick={() => onToggleAllColumns(false)}
|
onClick={() => onToggleAllColumns(false)}
|
||||||
className="text-xs px-2 py-0.5 rounded border border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400"
|
className="text-xs px-2 py-0.5 rounded border border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400"
|
||||||
>
|
>
|
||||||
|
|
@ -829,14 +785,6 @@ const WizardStep2 = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SqlTableDesignerDialog
|
|
||||||
isOpen={showTableDesignerDialog}
|
|
||||||
onClose={() => setShowTableDesignerDialog(false)}
|
|
||||||
dataSource={values.dataSourceCode}
|
|
||||||
initialTableData={null}
|
|
||||||
onDeployed={handleTableDeployed}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ export interface WizardGroupItem {
|
||||||
editorScript: string
|
editorScript: string
|
||||||
colSpan: number
|
colSpan: number
|
||||||
isRequired: boolean
|
isRequired: boolean
|
||||||
includeInEditingForm: boolean
|
|
||||||
turkishCaption?: string
|
turkishCaption?: string
|
||||||
englishCaption?: string
|
englishCaption?: string
|
||||||
captionName?: string
|
captionName?: string
|
||||||
|
|
@ -115,7 +114,6 @@ function newGroupItem(colName: string, meta?: DatabaseColumnDto): WizardGroupIte
|
||||||
editorScript: '',
|
editorScript: '',
|
||||||
colSpan: 1,
|
colSpan: 1,
|
||||||
isRequired: meta?.isNullable === false,
|
isRequired: meta?.isNullable === false,
|
||||||
includeInEditingForm: true,
|
|
||||||
turkishCaption: formatLabel(colName),
|
turkishCaption: formatLabel(colName),
|
||||||
englishCaption: formatLabel(colName),
|
englishCaption: formatLabel(colName),
|
||||||
captionName: `App.Listform.ListformField.${colName}`,
|
captionName: `App.Listform.ListformField.${colName}`,
|
||||||
|
|
@ -181,7 +179,6 @@ interface SortableItemProps {
|
||||||
onLookupQueryChange: (val: string) => void
|
onLookupQueryChange: (val: string) => void
|
||||||
onColSpanChange: (val: number) => void
|
onColSpanChange: (val: number) => void
|
||||||
onRequiredChange: (val: boolean) => void
|
onRequiredChange: (val: boolean) => void
|
||||||
onIncludeInEditingFormChange: (val: boolean) => void
|
|
||||||
onRemove: () => void
|
onRemove: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +194,6 @@ function SortableItem({
|
||||||
onEditorScriptChange,
|
onEditorScriptChange,
|
||||||
onColSpanChange,
|
onColSpanChange,
|
||||||
onRequiredChange,
|
onRequiredChange,
|
||||||
onIncludeInEditingFormChange,
|
|
||||||
onCaptionNameChange,
|
onCaptionNameChange,
|
||||||
onLookupDataSourceTypeChange,
|
onLookupDataSourceTypeChange,
|
||||||
onDisplayExprChange,
|
onDisplayExprChange,
|
||||||
|
|
@ -565,7 +561,7 @@ function SortableItem({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom row: ColSpan + Editing Form + Required */}
|
{/* Bottom row: ColSpan + Required */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="text-[10px] text-gray-400">
|
<span className="text-[10px] text-gray-400">
|
||||||
|
|
@ -583,20 +579,6 @@ function SortableItem({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<label
|
|
||||||
className="flex items-center gap-1 cursor-pointer ml-auto"
|
|
||||||
title={translate('::ListForms.Wizard.Step3.IncludeInEditingForm') || 'Editing form'}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={item.includeInEditingForm}
|
|
||||||
onChange={(e) => onIncludeInEditingFormChange(e.target.checked)}
|
|
||||||
className="w-3 h-3 accent-indigo-500"
|
|
||||||
/>
|
|
||||||
<span className="text-[10px] text-gray-400">
|
|
||||||
{translate('::ListForms.Wizard.Step3.IncludeInEditingForm') || 'Editing form'}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-1 cursor-pointer ml-auto" title="Required">
|
<label className="flex items-center gap-1 cursor-pointer ml-auto" title="Required">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
@ -741,9 +723,6 @@ function GroupCard({
|
||||||
onEditorScriptChange={(val) => onItemChange(item.id, { editorScript: val })}
|
onEditorScriptChange={(val) => onItemChange(item.id, { editorScript: val })}
|
||||||
onColSpanChange={(val) => onItemChange(item.id, { colSpan: val })}
|
onColSpanChange={(val) => onItemChange(item.id, { colSpan: val })}
|
||||||
onRequiredChange={(val) => onItemChange(item.id, { isRequired: val })}
|
onRequiredChange={(val) => onItemChange(item.id, { isRequired: val })}
|
||||||
onIncludeInEditingFormChange={(val) =>
|
|
||||||
onItemChange(item.id, { includeInEditingForm: val })
|
|
||||||
}
|
|
||||||
onLookupDataSourceTypeChange={(val: UiLookupDataSourceTypeEnum) =>
|
onLookupDataSourceTypeChange={(val: UiLookupDataSourceTypeEnum) =>
|
||||||
onItemChange(item.id, { lookupDataSourceType: val })
|
onItemChange(item.id, { lookupDataSourceType: val })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -462,10 +462,10 @@ function WizardStep4({
|
||||||
<p>Silmek istediğinize emin misiniz?</p>
|
<p>Silmek istediğinize emin misiniz?</p>
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
<Dialog.Footer className="flex justify-end gap-2">
|
<Dialog.Footer className="flex justify-end gap-2">
|
||||||
<Button type="button" variant="plain" onClick={() => setDeleteIndex(null)}>
|
<Button variant="plain" onClick={() => setDeleteIndex(null)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" variant="solid" onClick={removeSubForm}>
|
<Button variant="solid" onClick={removeSubForm}>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
|
|
|
||||||
|
|
@ -340,10 +340,10 @@ function WizardStep5({ widgets, translate, onChange, onBack, onNext }: Props) {
|
||||||
<p>Silmek istediğinize emin misiniz?</p>
|
<p>Silmek istediğinize emin misiniz?</p>
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
<Dialog.Footer className="flex justify-end gap-2">
|
<Dialog.Footer className="flex justify-end gap-2">
|
||||||
<Button type="button" variant="plain" onClick={() => setDeleteIndex(null)}>
|
<Button variant="plain" onClick={() => setDeleteIndex(null)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" variant="solid" onClick={removeWidget}>
|
<Button variant="solid" onClick={removeWidget}>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, Card, Checkbox, FormContainer, FormItem, Select } from '@/components/ui'
|
import { Button, Card, FormContainer, FormItem, Select } from '@/components/ui'
|
||||||
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||||
import { ListFormWorkflowCriteriaDto, WorkflowDto } from '@/proxy/form/models'
|
import { ListFormWorkflowCriteriaDto, WorkflowDto } from '@/proxy/form/models'
|
||||||
import { getUsers } from '@/services/identity.service'
|
import { getUsers } from '@/services/identity.service'
|
||||||
|
|
@ -9,8 +9,6 @@ import {
|
||||||
emptyCriteria,
|
emptyCriteria,
|
||||||
normalizeCriteria,
|
normalizeCriteria,
|
||||||
toCriteriaForm,
|
toCriteriaForm,
|
||||||
uniqueCriteriaId,
|
|
||||||
uniqueCriteriaTitle,
|
|
||||||
type WorkflowCriteriaForm,
|
type WorkflowCriteriaForm,
|
||||||
} from '@/utils/workflow/workflowHelpers'
|
} from '@/utils/workflow/workflowHelpers'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
|
|
@ -18,7 +16,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import type { FormEvent } from 'react'
|
import type { FormEvent } from 'react'
|
||||||
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
|
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
|
||||||
import { WorkflowDesigner } from '../workflow/WorkflowDesigner'
|
import { WorkflowDesigner } from '../workflow/WorkflowDesigner'
|
||||||
import { IdentityUserDto } from '@/proxy/admin/models'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -47,6 +44,8 @@ const toDesignerCriteria = (items: ListFormWorkflowCriteriaDto[]): WorkflowCrite
|
||||||
const toWizardCriteria = (items: WorkflowCriteriaDto[]): ListFormWorkflowCriteriaDto[] =>
|
const toWizardCriteria = (items: WorkflowCriteriaDto[]): ListFormWorkflowCriteriaDto[] =>
|
||||||
items.map(({ nodeId: _nodeId, ...item }) => item)
|
items.map(({ nodeId: _nodeId, ...item }) => item)
|
||||||
|
|
||||||
|
const nextId = () => `WF${Date.now()}${Math.floor(Math.random() * 1000)}`
|
||||||
|
|
||||||
function WizardStep6({
|
function WizardStep6({
|
||||||
listFormCode,
|
listFormCode,
|
||||||
workflow,
|
workflow,
|
||||||
|
|
@ -78,7 +77,7 @@ function WizardStep6({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUsers(0, 1000).then((response) => {
|
getUsers(0, 1000).then((response) => {
|
||||||
setUserList(
|
setUserList(
|
||||||
(response.data?.items ?? []).map((user: IdentityUserDto) => ({
|
(response.data?.items ?? []).map((user: any) => ({
|
||||||
value: user.userName,
|
value: user.userName,
|
||||||
label: `${user.userName} (${user.name} ${user.surname})`,
|
label: `${user.userName} (${user.name} ${user.surname})`,
|
||||||
})),
|
})),
|
||||||
|
|
@ -96,10 +95,9 @@ function WizardStep6({
|
||||||
const saveCriteria = (event: FormEvent<HTMLFormElement>) => {
|
const saveCriteria = (event: FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const normalized = normalizeCriteria({ ...criteriaForm, listFormCode })
|
const normalized = normalizeCriteria({ ...criteriaForm, listFormCode })
|
||||||
const id = normalized.id || uniqueCriteriaId(currentCriteria)
|
const id = normalized.id || nextId()
|
||||||
|
const nextItem = { ...normalized, id, nodeId: id } as WorkflowCriteriaDto
|
||||||
const exists = currentCriteria.some((item) => item.id === id)
|
const exists = currentCriteria.some((item) => item.id === id)
|
||||||
const title = uniqueCriteriaTitle(normalized.kind || '', currentCriteria, id, normalized.title)
|
|
||||||
const nextItem = { ...normalized, id, nodeId: id, title } as WorkflowCriteriaDto
|
|
||||||
updateCriteria(
|
updateCriteria(
|
||||||
exists
|
exists
|
||||||
? currentCriteria.map((item) => (item.id === id ? nextItem : item))
|
? currentCriteria.map((item) => (item.id === id ? nextItem : item))
|
||||||
|
|
@ -110,12 +108,11 @@ function WizardStep6({
|
||||||
}
|
}
|
||||||
|
|
||||||
const addCriteria = (kind: string) => {
|
const addCriteria = (kind: string) => {
|
||||||
const id = uniqueCriteriaId(currentCriteria)
|
const id = nextId()
|
||||||
const nextItem = {
|
const nextItem = {
|
||||||
...normalizeCriteria(emptyCriteria(kind, listFormCode)),
|
...normalizeCriteria(emptyCriteria(kind, listFormCode)),
|
||||||
id,
|
id,
|
||||||
nodeId: id,
|
nodeId: id,
|
||||||
title: uniqueCriteriaTitle(kind, currentCriteria),
|
|
||||||
positionX: 80 + (currentCriteria.length % 5) * 230,
|
positionX: 80 + (currentCriteria.length % 5) * 230,
|
||||||
positionY: 220 + Math.floor(currentCriteria.length / 5) * 140,
|
positionY: 220 + Math.floor(currentCriteria.length / 5) * 140,
|
||||||
} as WorkflowCriteriaDto
|
} as WorkflowCriteriaDto
|
||||||
|
|
@ -195,15 +192,14 @@ function WizardStep6({
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetDemo = () => {
|
const resetDemo = () => {
|
||||||
const startId = uniqueCriteriaId([])
|
const startId = nextId()
|
||||||
const approvalId = uniqueCriteriaId([], [startId])
|
const approvalId = nextId()
|
||||||
const endId = uniqueCriteriaId([], [startId, approvalId])
|
const endId = nextId()
|
||||||
updateCriteria([
|
updateCriteria([
|
||||||
{
|
{
|
||||||
...normalizeCriteria(emptyCriteria('Start', listFormCode)),
|
...normalizeCriteria(emptyCriteria('Start', listFormCode)),
|
||||||
id: startId,
|
id: startId,
|
||||||
nodeId: startId,
|
nodeId: startId,
|
||||||
title: uniqueCriteriaTitle('Start', []),
|
|
||||||
nextOnStart: approvalId,
|
nextOnStart: approvalId,
|
||||||
positionX: 72,
|
positionX: 72,
|
||||||
positionY: 160,
|
positionY: 160,
|
||||||
|
|
@ -212,7 +208,6 @@ function WizardStep6({
|
||||||
...normalizeCriteria(emptyCriteria('Approval', listFormCode)),
|
...normalizeCriteria(emptyCriteria('Approval', listFormCode)),
|
||||||
id: approvalId,
|
id: approvalId,
|
||||||
nodeId: approvalId,
|
nodeId: approvalId,
|
||||||
title: uniqueCriteriaTitle('Approval', []),
|
|
||||||
nextOnApprove: endId,
|
nextOnApprove: endId,
|
||||||
positionX: 360,
|
positionX: 360,
|
||||||
positionY: 160,
|
positionY: 160,
|
||||||
|
|
@ -221,7 +216,6 @@ function WizardStep6({
|
||||||
...normalizeCriteria(emptyCriteria('End', listFormCode)),
|
...normalizeCriteria(emptyCriteria('End', listFormCode)),
|
||||||
id: endId,
|
id: endId,
|
||||||
nodeId: endId,
|
nodeId: endId,
|
||||||
title: uniqueCriteriaTitle('End', []),
|
|
||||||
positionX: 650,
|
positionX: 650,
|
||||||
positionY: 160,
|
positionY: 160,
|
||||||
} as WorkflowCriteriaDto,
|
} as WorkflowCriteriaDto,
|
||||||
|
|
@ -240,7 +234,7 @@ function WizardStep6({
|
||||||
<Form>
|
<Form>
|
||||||
<Card className="mb-4" header={translate('::ListForms.ListFormEdit.TabWorkflow')}>
|
<Card className="mb-4" header={translate('::ListForms.ListFormEdit.TabWorkflow')}>
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
<div className="grid grid-cols-4 gap-4">
|
||||||
{[
|
{[
|
||||||
[
|
[
|
||||||
'approvalUserFieldName',
|
'approvalUserFieldName',
|
||||||
|
|
@ -283,26 +277,6 @@ function WizardStep6({
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
))}
|
))}
|
||||||
<FormItem
|
|
||||||
label={translate('::ListForms.ListFormEdit.Workflow.IsFilterUserName')}
|
|
||||||
>
|
|
||||||
<Field name="isFilterUserName">
|
|
||||||
{({ field, form }: FieldProps<boolean>) => (
|
|
||||||
<Checkbox
|
|
||||||
name={field.name}
|
|
||||||
checked={Boolean(values.isFilterUserName)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
form.setFieldValue(field.name, checked)
|
|
||||||
onWorkflowChange({
|
|
||||||
...values,
|
|
||||||
isFilterUserName: checked,
|
|
||||||
criteria,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
|
||||||
</div>
|
</div>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -226,13 +226,6 @@ const WizardStep7 = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
||||||
const editingFormFields = groups.flatMap((g) =>
|
|
||||||
g.items
|
|
||||||
.filter((item) => item.includeInEditingForm && item.dataField !== values.keyFieldName)
|
|
||||||
.map((item) => ({ ...item, groupCaption: g.caption })),
|
|
||||||
)
|
|
||||||
const groupedFieldNames = new Set(groups.flatMap((g) => g.items.map((item) => item.dataField)))
|
|
||||||
const ungroupedSelectedColumns = [...selectedColumns].filter((col) => !groupedFieldNames.has(col))
|
|
||||||
const hasWorkflowFields = Boolean(
|
const hasWorkflowFields = Boolean(
|
||||||
workflow.approvalUserFieldName ||
|
workflow.approvalUserFieldName ||
|
||||||
workflow.approvalDateFieldName ||
|
workflow.approvalDateFieldName ||
|
||||||
|
|
@ -305,157 +298,91 @@ const WizardStep7 = ({
|
||||||
label={translate('::App.Listform.ListformField.ConnectionString')}
|
label={translate('::App.Listform.ListformField.ConnectionString')}
|
||||||
value={values.dataSourceConnectionString}
|
value={values.dataSourceConnectionString}
|
||||||
/>
|
/>
|
||||||
|
<Row
|
||||||
|
label={translate('::ListForms.Wizard.Step4.CommandType')}
|
||||||
|
value={
|
||||||
|
selectCommandTypeOptions.find((o) => o.value === values.selectCommandType)?.label ||
|
||||||
|
values.selectCommandType
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Row
|
<Row
|
||||||
label={translate('::ListForms.Wizard.Step4.SelectCommand')}
|
label={translate('::ListForms.Wizard.Step4.SelectCommand')}
|
||||||
value={`${values.selectCommand} (${
|
value={values.selectCommand}
|
||||||
selectCommandTypeOptions.find((o: any) => o.value === values.selectCommandType)
|
|
||||||
?.label ?? String(values.selectCommandType)
|
|
||||||
})`}
|
|
||||||
/>
|
/>
|
||||||
<Row
|
<Row
|
||||||
label={translate('::ListForms.Wizard.Step4.KeyField')}
|
label={translate('::ListForms.Wizard.Step4.KeyField')}
|
||||||
value={`${values.keyFieldName} (${
|
value={values.keyFieldName}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
label={translate('::ListForms.Wizard.Step4.KeyFieldType')}
|
||||||
|
value={
|
||||||
dbSourceTypeOptions.find((o: any) => o.value === values.keyFieldDbSourceType)
|
dbSourceTypeOptions.find((o: any) => o.value === values.keyFieldDbSourceType)
|
||||||
?.label ?? String(values.keyFieldDbSourceType)
|
?.label ?? String(values.keyFieldDbSourceType)
|
||||||
})`}
|
}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Section
|
<Section
|
||||||
title={
|
title={translate('::ListForms.Wizard.Step4.SelectedColumns')}
|
||||||
translate('::ListForms.Wizard.Step4.ColumnsAndFormLayout') || 'Columns & Form Layout'
|
badge={selectedColumns.size}
|
||||||
}
|
|
||||||
badge={`${selectedColumns.size} ${translate('::App.Listform.ListformField.Column')} / ${editingFormFields.length} ${translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form'}`}
|
|
||||||
>
|
>
|
||||||
<div className="mb-3 grid grid-cols-3 gap-2">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{[
|
{[...selectedColumns].map((col) => {
|
||||||
{
|
const meta = selectCommandColumns.find((c) => c.columnName === col)
|
||||||
label: translate('::ListForms.Wizard.Step4.SelectedColumns'),
|
return (
|
||||||
value: selectedColumns.size,
|
<span
|
||||||
className: 'text-indigo-600 dark:text-indigo-400',
|
key={col}
|
||||||
},
|
className="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 border border-indigo-200 dark:border-indigo-700"
|
||||||
{
|
>
|
||||||
label: translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form',
|
{col}
|
||||||
value: editingFormFields.length,
|
{meta?.dataType && (
|
||||||
className: 'text-emerald-600 dark:text-emerald-400',
|
<span className="text-[10px] text-indigo-400 opacity-70">{meta.dataType}</span>
|
||||||
},
|
)}
|
||||||
{
|
</span>
|
||||||
label: translate('::ListForms.Wizard.Step4.FormGroups'),
|
)
|
||||||
value: groups.length,
|
})}
|
||||||
className: 'text-gray-700 dark:text-gray-200',
|
|
||||||
},
|
|
||||||
].map((item) => (
|
|
||||||
<div
|
|
||||||
key={item.label}
|
|
||||||
className="rounded-lg border border-gray-100 dark:border-gray-800 bg-gray-50 dark:bg-gray-800 px-3 py-2"
|
|
||||||
>
|
|
||||||
<div className={`text-lg font-semibold leading-none ${item.className}`}>
|
|
||||||
{item.value}
|
|
||||||
</div>
|
|
||||||
<div className="mt-1 text-[11px] text-gray-400 truncate">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<Section title={translate('::ListForms.Wizard.Step4.FormGroups')} badge={groups.length}>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
{groups.map((g) => (
|
{groups.map((g) => (
|
||||||
<div
|
<Section
|
||||||
key={g.id}
|
key={g.id}
|
||||||
className="overflow-hidden rounded-lg border border-gray-100 dark:border-gray-800"
|
title={g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
|
||||||
|
badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::App.Listform.ListformField.Column')}`}
|
||||||
|
defaultOpen={false}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-3 bg-gray-50 dark:bg-gray-800 px-3 py-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<span className="text-xs font-semibold text-gray-700 dark:text-gray-200 truncate">
|
|
||||||
{g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
|
|
||||||
</span>
|
|
||||||
<div className="flex items-center gap-1.5 text-[10px] text-gray-400 shrink-0">
|
|
||||||
<span className="rounded bg-white dark:bg-gray-900 px-1.5 py-0.5">
|
|
||||||
{g.items.length} {translate('::ListForms.Wizard.Step4.StatField')}
|
|
||||||
</span>
|
|
||||||
<span className="rounded bg-white dark:bg-gray-900 px-1.5 py-0.5">
|
|
||||||
{g.colCount} {translate('::App.Listform.ListformField.Column')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="divide-y divide-gray-100 dark:divide-gray-800">
|
|
||||||
{g.items.length === 0 ? (
|
{g.items.length === 0 ? (
|
||||||
<span className="block px-3 py-2 text-xs text-gray-300 italic">
|
<span className="text-xs text-gray-300 italic">
|
||||||
{translate('::ListForms.Wizard.Step4.NoFields') || 'Alan yok'}
|
{translate('::ListForms.Wizard.Step4.NoFields') || 'Alan yok'}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
g.items.map((item) => {
|
g.items.map((item) => (
|
||||||
const meta = selectCommandColumns.find((c) => c.columnName === item.dataField)
|
<div
|
||||||
const isKeyField = item.dataField === values.keyFieldName
|
key={item.id}
|
||||||
const isPopupField = item.includeInEditingForm && !isKeyField
|
className="flex items-center gap-2 py-1 border-b border-gray-100 dark:border-gray-800 last:border-0"
|
||||||
return (
|
>
|
||||||
<div
|
<span className="text-xs font-medium text-indigo-600 dark:text-indigo-400 w-36 shrink-0 truncate">
|
||||||
key={item.id}
|
{item.dataField}
|
||||||
className="grid grid-cols-[minmax(150px,1fr)_auto] items-center gap-3 px-3 py-1.5"
|
</span>
|
||||||
>
|
<span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
{item.editorType}
|
||||||
<span className="text-xs font-medium text-indigo-600 dark:text-indigo-400 truncate">
|
</span>
|
||||||
{item.dataField}
|
<span className="text-[10px] text-gray-400 mr-auto shrink-0 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
|
||||||
</span>
|
col-span-{item.colSpan}
|
||||||
{meta?.dataType && (
|
{item.isRequired && (
|
||||||
<span className="text-[10px] text-gray-400 truncate">
|
<span className="ml-1 text-red-400 font-semibold">*</span>
|
||||||
{meta.dataType}
|
)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
</div>
|
||||||
</div>
|
))
|
||||||
<div className="flex flex-wrap justify-end gap-1">
|
|
||||||
<span className="rounded bg-indigo-50 dark:bg-indigo-900/30 px-1.5 py-0.5 text-[10px] text-indigo-600 dark:text-indigo-300">
|
|
||||||
{translate('::ListForms.Wizard.Step4.SelectedColumns') || 'List'}
|
|
||||||
</span>
|
|
||||||
{isPopupField && (
|
|
||||||
<span className="rounded bg-emerald-50 dark:bg-emerald-900/30 px-1.5 py-0.5 text-[10px] text-emerald-600 dark:text-emerald-300">
|
|
||||||
{translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form'}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isKeyField && (
|
|
||||||
<span className="rounded bg-amber-50 dark:bg-amber-900/30 px-1.5 py-0.5 text-[10px] text-amber-600 dark:text-amber-300">
|
|
||||||
{translate('::ListForms.Wizard.Step4.KeyField')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className="rounded bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 text-[10px] text-gray-400">
|
|
||||||
{item.editorType}
|
|
||||||
</span>
|
|
||||||
<span className="rounded bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 text-[10px] text-gray-400">
|
|
||||||
span-{item.colSpan}
|
|
||||||
{item.isRequired && <span className="ml-1 text-red-400">*</span>}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Section>
|
||||||
))}
|
))}
|
||||||
{ungroupedSelectedColumns.length > 0 && (
|
|
||||||
<div className="rounded-lg border border-amber-100 dark:border-amber-900/40 bg-amber-50/50 dark:bg-amber-900/10 px-3 py-2">
|
|
||||||
<div className="mb-1.5 text-xs font-semibold text-amber-700 dark:text-amber-300">
|
|
||||||
{translate('::ListForms.Wizard.Step4.UngroupedColumns') || 'Ungrouped columns'}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap gap-1.5">
|
|
||||||
{ungroupedSelectedColumns.map((col) => {
|
|
||||||
const meta = selectCommandColumns.find((c) => c.columnName === col)
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
key={col}
|
|
||||||
className="inline-flex items-center gap-1 rounded-full border border-amber-200 dark:border-amber-800 bg-white dark:bg-gray-900 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-300"
|
|
||||||
>
|
|
||||||
{col}
|
|
||||||
{meta?.dataType && (
|
|
||||||
<span className="text-[10px] text-amber-500 opacity-70">
|
|
||||||
{meta.dataType}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
|
@ -533,7 +460,6 @@ const WizardStep7 = ({
|
||||||
<Row label="Approval Date" value={workflow.approvalDateFieldName} />
|
<Row label="Approval Date" value={workflow.approvalDateFieldName} />
|
||||||
<Row label="Approval Status" value={workflow.approvalStatusFieldName} />
|
<Row label="Approval Status" value={workflow.approvalStatusFieldName} />
|
||||||
<Row label="Approval Description" value={workflow.approvalDescriptionFieldName} />
|
<Row label="Approval Description" value={workflow.approvalDescriptionFieldName} />
|
||||||
<Row label="Is Filter User Name?" value={workflow.isFilterUserName === true ? 'Yes' : 'No'} />
|
|
||||||
</div>
|
</div>
|
||||||
{workflowItems.length > 0 && (
|
{workflowItems.length > 0 && (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
@ -545,18 +471,12 @@ const WizardStep7 = ({
|
||||||
<div className="text-xs font-semibold text-gray-700 dark:text-gray-200">
|
<div className="text-xs font-semibold text-gray-700 dark:text-gray-200">
|
||||||
{criteria.title || criteria.kind || `Criteria ${index + 1}`}
|
{criteria.title || criteria.kind || `Criteria ${index + 1}`}
|
||||||
</div>
|
</div>
|
||||||
{criteria.kind === 'Compare' && (
|
<div className="mt-1 text-[11px] text-gray-500 dark:text-gray-400">
|
||||||
<div className="mt-1 text-[11px] text-gray-500 dark:text-gray-400">
|
{criteria.compareColumn} {criteria.compareOperator} {criteria.compareValue}
|
||||||
{criteria.compareColumn} {criteria.compareOperator}{' '}
|
</div>
|
||||||
{criteria.compareValue}
|
<div className="text-[11px] text-gray-500 dark:text-gray-400">
|
||||||
</div>
|
Approver: {criteria.approver}
|
||||||
)}
|
</div>
|
||||||
{(criteria.kind === 'Approval' || criteria.kind === 'Inform') &&
|
|
||||||
criteria.approver && (
|
|
||||||
<div className="text-[11px] text-gray-500 dark:text-gray-400">
|
|
||||||
Approver: {criteria.approver}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -569,14 +489,10 @@ const WizardStep7 = ({
|
||||||
{/* ── Right: Deploy ──────────────────────────────────────────── */}
|
{/* ── Right: Deploy ──────────────────────────────────────────── */}
|
||||||
<div className="sticky top-4 flex flex-col gap-3 max-h-[calc(100vh-200px)]">
|
<div className="sticky top-4 flex flex-col gap-3 max-h-[calc(100vh-200px)]">
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-7 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
{[
|
{[
|
||||||
{ label: translate('::ListForms.Wizard.Step4.StatGroup'), value: groups.length },
|
{ label: translate('::ListForms.Wizard.Step4.StatGroup'), value: groups.length },
|
||||||
{ label: translate('::ListForms.Wizard.Step4.StatField'), value: totalFields },
|
{ label: translate('::ListForms.Wizard.Step4.StatField'), value: totalFields },
|
||||||
{
|
|
||||||
label: translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form',
|
|
||||||
value: editingFormFields.length,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: translate('::App.Listform.ListformField.Column'),
|
label: translate('::App.Listform.ListformField.Column'),
|
||||||
value: selectedColumns.size,
|
value: selectedColumns.size,
|
||||||
|
|
|
||||||
|
|
@ -257,19 +257,17 @@ export function WorkflowCriteria({
|
||||||
onChange={(event) => setField('title', event.target.value)}
|
onChange={(event) => setField('title', event.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
{(formValues.kind === 'Approval' || formValues.kind === 'Inform') && (
|
<FormItem
|
||||||
<FormItem
|
label={translate('::App.Listform.ListformField.Approver')}
|
||||||
label={translate('::App.Listform.ListformField.Approver')}
|
asterisk={formValues.kind === 'Approval' || formValues.kind === 'Inform'}
|
||||||
asterisk
|
>
|
||||||
>
|
<SelectField
|
||||||
<SelectField
|
required={formValues.kind === 'Approval' || formValues.kind === 'Inform'}
|
||||||
required
|
options={userList}
|
||||||
options={userList}
|
value={formValues.approver}
|
||||||
value={formValues.approver}
|
onChange={(value) => setField('approver', value)}
|
||||||
onChange={(value) => setField('approver', value)}
|
/>
|
||||||
/>
|
</FormItem>
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(formValues.kind === 'Start' || formValues.kind === 'Inform') && (
|
{(formValues.kind === 'Start' || formValues.kind === 'Inform') && (
|
||||||
<FormItem
|
<FormItem
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ import {
|
||||||
deleteClaimUser,
|
deleteClaimUser,
|
||||||
getUserDetail,
|
getUserDetail,
|
||||||
postClaimUser,
|
postClaimUser,
|
||||||
putUserAvatar,
|
|
||||||
putUserDetail,
|
putUserDetail,
|
||||||
putUserLookout,
|
putUserLookout,
|
||||||
putUserPermission,
|
putUserPermission,
|
||||||
} from '@/services/identity.service'
|
} from '@/services/identity.service'
|
||||||
|
import { updateProfile } from '@/services/account.service'
|
||||||
import { CountryDto, getCountry } from '@/services/home.service'
|
import { CountryDto, getCountry } from '@/services/home.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
@ -97,7 +97,6 @@ function UserDetails() {
|
||||||
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
||||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||||
const [image, setImage] = useState<string | undefined>()
|
const [image, setImage] = useState<string | undefined>()
|
||||||
const [hasAvatarChange, setHasAvatarChange] = useState(false)
|
|
||||||
const cropperRef = useRef<CropperRef>(null)
|
const cropperRef = useRef<CropperRef>(null)
|
||||||
const previewRef = useRef<CropperPreviewRef>(null)
|
const previewRef = useRef<CropperPreviewRef>(null)
|
||||||
const auth = useStoreState((state) => state.auth)
|
const auth = useStoreState((state) => state.auth)
|
||||||
|
|
@ -112,26 +111,15 @@ function UserDetails() {
|
||||||
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
||||||
|
|
||||||
const getUser = async (syncAvatar = true) => {
|
const getUser = async (syncAvatar = true) => {
|
||||||
if (!userId) {
|
const { data } = await getUserDetail(userId || '')
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await getUserDetail(userId)
|
|
||||||
setUserDetails(data)
|
setUserDetails(data)
|
||||||
if (syncAvatar) {
|
if (syncAvatar) {
|
||||||
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
||||||
setHasAvatarChange(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUserDetails(undefined)
|
|
||||||
setImage(undefined)
|
|
||||||
setHasAvatarChange(false)
|
|
||||||
getUser()
|
getUser()
|
||||||
}, [userId])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getCountry().then(({ data }) => setCountries(data))
|
getCountry().then(({ data }) => setCountries(data))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
@ -143,10 +131,8 @@ function UserDetails() {
|
||||||
const onChooseImage = async (file: File[]) => {
|
const onChooseImage = async (file: File[]) => {
|
||||||
if (file[0]) {
|
if (file[0]) {
|
||||||
setImage(URL.createObjectURL(file[0]))
|
setImage(URL.createObjectURL(file[0]))
|
||||||
setHasAvatarChange(true)
|
|
||||||
} else {
|
} else {
|
||||||
setImage(undefined)
|
setImage(undefined)
|
||||||
setHasAvatarChange(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,49 +246,34 @@ function UserDetails() {
|
||||||
<TabContent value="user">
|
<TabContent value="user">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize
|
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
await putUserDetail({ ...values })
|
await putUserDetail({ ...values })
|
||||||
if (values.id === auth.user.id) {
|
|
||||||
setUser({
|
|
||||||
...auth.user,
|
|
||||||
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
let keepCurrentAvatar = false
|
let keepCurrentAvatar = false
|
||||||
|
|
||||||
if (hasAvatarChange) {
|
const avatar = await getCroppedAvatar()
|
||||||
const avatar = await getCroppedAvatar()
|
const resp = await updateProfile({
|
||||||
const resp = await putUserAvatar({
|
name: values.name ?? '',
|
||||||
userId: values.id!,
|
surname: values.surname ?? '',
|
||||||
avatar: avatar ? new File([avatar], 'avatar') : undefined,
|
avatar: avatar ? new File([avatar], 'avatar') : undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const avatarUrl =
|
||||||
|
AVATAR_URL(auth.user.id, auth.tenant?.tenantId) + `?${dayjs().unix()}`
|
||||||
|
|
||||||
|
setUser({
|
||||||
|
...auth.user,
|
||||||
|
name: `${resp.data.name} ${resp.data.surname}`.trim(),
|
||||||
|
avatar: avatarUrl,
|
||||||
|
})
|
||||||
|
setImage(avatarUrl)
|
||||||
|
keepCurrentAvatar = true
|
||||||
|
} else {
|
||||||
|
toast.push(<Notification title={resp?.error?.message} type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (resp.status === 200) {
|
|
||||||
const avatarUrl =
|
|
||||||
AVATAR_URL(values.id, values.tenantId) + `?${dayjs().unix()}`
|
|
||||||
|
|
||||||
if (values.id === auth.user.id) {
|
|
||||||
setUser({
|
|
||||||
...auth.user,
|
|
||||||
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
|
||||||
avatar: avatarUrl,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setImage(avatarUrl)
|
|
||||||
setHasAvatarChange(false)
|
|
||||||
keepCurrentAvatar = true
|
|
||||||
} else {
|
|
||||||
const errorMessage =
|
|
||||||
(resp as { error?: { message?: string } })?.error?.message || 'Hata'
|
|
||||||
|
|
||||||
toast.push(<Notification title={errorMessage} type="danger" />, {
|
|
||||||
placement: 'top-end',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -571,10 +542,7 @@ function UserDetails() {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
icon={<FaTrashAlt />}
|
icon={<FaTrashAlt />}
|
||||||
onClick={() => {
|
onClick={() => setImage(undefined)}
|
||||||
setImage(undefined)
|
|
||||||
setHasAvatarChange(true)
|
|
||||||
}}
|
|
||||||
></Button>
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
{image && (
|
{image && (
|
||||||
|
|
@ -606,7 +574,6 @@ function UserDetails() {
|
||||||
<TabContent value="permission">
|
<TabContent value="permission">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize
|
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { setSubmitting }) => {
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -718,7 +685,6 @@ function UserDetails() {
|
||||||
<TabContent value="work">
|
<TabContent value="work">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize
|
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -900,7 +866,6 @@ function UserDetails() {
|
||||||
<TabContent value="identity">
|
<TabContent value="identity">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize
|
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -1257,7 +1222,6 @@ function UserDetails() {
|
||||||
<TabContent value="lockout">
|
<TabContent value="lockout">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize
|
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { setSubmitting }) => {
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ type SqlDataType =
|
||||||
| 'decimal'
|
| 'decimal'
|
||||||
| 'float'
|
| 'float'
|
||||||
| 'bit'
|
| 'bit'
|
||||||
| 'datetime'
|
|
||||||
| 'datetime2'
|
| 'datetime2'
|
||||||
| 'date'
|
| 'date'
|
||||||
| 'uniqueidentifier'
|
| 'uniqueidentifier'
|
||||||
|
|
@ -69,7 +68,7 @@ interface TableDesignerDialogProps {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
dataSource: string | null
|
dataSource: string | null
|
||||||
onDeployed?: (table: { schemaName: string; tableName: string }) => void | Promise<void>
|
onDeployed?: () => void
|
||||||
initialTableData?: { schemaName: string; tableName: string } | null
|
initialTableData?: { schemaName: string; tableName: string } | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +98,6 @@ const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
||||||
{ value: 'decimal', label: 'Decimal (decimal 18,4)' },
|
{ value: 'decimal', label: 'Decimal (decimal 18,4)' },
|
||||||
{ value: 'float', label: 'Float (float)' },
|
{ value: 'float', label: 'Float (float)' },
|
||||||
{ value: 'bit', label: 'Bool (bit)' },
|
{ value: 'bit', label: 'Bool (bit)' },
|
||||||
{ value: 'datetime', label: 'DateTime (datetime)' },
|
|
||||||
{ value: 'datetime2', label: 'DateTime (datetime2)' },
|
{ value: 'datetime2', label: 'DateTime (datetime2)' },
|
||||||
{ value: 'date', label: 'Date (date)' },
|
{ value: 'date', label: 'Date (date)' },
|
||||||
{ value: 'uniqueidentifier', label: 'Guid (uniqueidentifier)' },
|
{ value: 'uniqueidentifier', label: 'Guid (uniqueidentifier)' },
|
||||||
|
|
@ -228,45 +226,6 @@ const TENANT_COLUMN: ColumnDefinition = {
|
||||||
description: 'Tenant ID for multi-tenancy',
|
description: 'Tenant ID for multi-tenancy',
|
||||||
}
|
}
|
||||||
|
|
||||||
const WORKFLOW_COLUMNS: ColumnDefinition[] = [
|
|
||||||
{
|
|
||||||
id: '__ApprovalUserId',
|
|
||||||
columnName: 'ApprovalUserName',
|
|
||||||
dataType: 'nvarchar',
|
|
||||||
maxLength: '256',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: '',
|
|
||||||
description: 'Workflow approval user name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '__ApprovalStatus',
|
|
||||||
columnName: 'ApprovalStatus',
|
|
||||||
dataType: 'nvarchar',
|
|
||||||
maxLength: '50',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: '',
|
|
||||||
description: 'Workflow approval status',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '__ApprovalDate',
|
|
||||||
columnName: 'ApprovalDate',
|
|
||||||
dataType: 'datetime',
|
|
||||||
maxLength: '',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: '',
|
|
||||||
description: 'Workflow approval date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '__ApprovalDescription',
|
|
||||||
columnName: 'ApprovalDescription',
|
|
||||||
dataType: 'nvarchar',
|
|
||||||
maxLength: '200',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: '',
|
|
||||||
description: 'Workflow approval description',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const CREATE_TABLE_SCRIPT_STORAGE_KEY = 'sqlQueryManager.lastCreateTableScript'
|
const CREATE_TABLE_SCRIPT_STORAGE_KEY = 'sqlQueryManager.lastCreateTableScript'
|
||||||
|
|
||||||
// ─── T-SQL Generator ──────────────────────────────────────────────────────────
|
// ─── T-SQL Generator ──────────────────────────────────────────────────────────
|
||||||
|
|
@ -446,8 +405,6 @@ function dbColToColumnDef(col: {
|
||||||
dataType = 'float'
|
dataType = 'float'
|
||||||
} else if (dt === 'bit') {
|
} else if (dt === 'bit') {
|
||||||
dataType = 'bit'
|
dataType = 'bit'
|
||||||
} else if (dt === 'datetime') {
|
|
||||||
dataType = 'datetime'
|
|
||||||
} else if (dt.startsWith('datetime') || dt === 'smalldatetime') {
|
} else if (dt.startsWith('datetime') || dt === 'smalldatetime') {
|
||||||
dataType = 'datetime2'
|
dataType = 'datetime2'
|
||||||
} else if (dt === 'date') {
|
} else if (dt === 'date') {
|
||||||
|
|
@ -519,7 +476,6 @@ function mapSqlTypeToDesigner(dataTypeRaw: string, lengthRaw: string): {
|
||||||
if (dt === 'decimal' || dt === 'numeric') return { dataType: 'decimal', maxLength: '' }
|
if (dt === 'decimal' || dt === 'numeric') return { dataType: 'decimal', maxLength: '' }
|
||||||
if (dt === 'float' || dt === 'real') return { dataType: 'float', maxLength: '' }
|
if (dt === 'float' || dt === 'real') return { dataType: 'float', maxLength: '' }
|
||||||
if (dt === 'bit') return { dataType: 'bit', maxLength: '' }
|
if (dt === 'bit') return { dataType: 'bit', maxLength: '' }
|
||||||
if (dt === 'datetime') return { dataType: 'datetime', maxLength: '' }
|
|
||||||
if (dt.startsWith('datetime') || dt === 'smalldatetime' || dt === 'time') {
|
if (dt.startsWith('datetime') || dt === 'smalldatetime' || dt === 'time') {
|
||||||
return { dataType: 'datetime2', maxLength: '' }
|
return { dataType: 'datetime2', maxLength: '' }
|
||||||
}
|
}
|
||||||
|
|
@ -1268,18 +1224,6 @@ const SqlTableDesignerDialog = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addWorkflowColumns = () => {
|
|
||||||
const existingNames = new Set(columns.map((c) => c.columnName.trim().toLowerCase()))
|
|
||||||
const toAdd = WORKFLOW_COLUMNS.filter((c) => !existingNames.has(c.columnName.toLowerCase()))
|
|
||||||
|
|
||||||
if (toAdd.length > 0) {
|
|
||||||
setColumns((prev) => {
|
|
||||||
const nonEmpty = prev.filter((c) => c.columnName.trim() !== '')
|
|
||||||
return [...nonEmpty, ...toAdd.map((c) => ({ ...c })), createEmptyColumn()]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const importColumnsFromRememberedCreateTable = async () => {
|
const importColumnsFromRememberedCreateTable = async () => {
|
||||||
let script = ''
|
let script = ''
|
||||||
|
|
||||||
|
|
@ -1647,10 +1591,7 @@ const SqlTableDesignerDialog = ({
|
||||||
} catch {
|
} catch {
|
||||||
// Non-blocking: seed file save failure does not affect deploy success
|
// Non-blocking: seed file save failure does not affect deploy success
|
||||||
}
|
}
|
||||||
await onDeployed?.({
|
onDeployed?.()
|
||||||
schemaName: initialTableData?.schemaName || 'dbo',
|
|
||||||
tableName: deployedTable,
|
|
||||||
})
|
|
||||||
handleClose()
|
handleClose()
|
||||||
} else {
|
} else {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -1749,9 +1690,6 @@ const SqlTableDesignerDialog = ({
|
||||||
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
||||||
{translate('::App.SqlQueryManager.AddMultiTenantColumns')}
|
{translate('::App.SqlQueryManager.AddMultiTenantColumns')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="xs" variant="solid" color="indigo-600" onClick={addWorkflowColumns}>
|
|
||||||
{translate('::App.SqlQueryManager.AddWorkflowColumns')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
|
@ -2005,14 +1943,7 @@ const SqlTableDesignerDialog = ({
|
||||||
initialParentCode={selectedMenuCode}
|
initialParentCode={selectedMenuCode}
|
||||||
initialOrder={999}
|
initialOrder={999}
|
||||||
rawItems={rawMenuItems}
|
rawItems={rawMenuItems}
|
||||||
onSaved={(menu) =>
|
onSaved={() => reloadMenus()}
|
||||||
reloadMenus((items) => {
|
|
||||||
const savedMenu = items.find((item) => item.code === menu.code)
|
|
||||||
if (savedMenu?.code && savedMenu.shortName) {
|
|
||||||
onMenuCodeSelect(savedMenu.code)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -2740,7 +2671,7 @@ const SqlTableDesignerDialog = ({
|
||||||
// ── Render ─────────────────────────────────────────────────────────────────
|
// ── Render ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={1100}>
|
<Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={900}>
|
||||||
<Dialog.Body className="flex flex-col gap-2">
|
<Dialog.Body className="flex flex-col gap-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center gap-3 border-b pb-3 flex-shrink-0">
|
<div className="flex items-center gap-3 border-b pb-3 flex-shrink-0">
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,16 @@ import TabNav from '@/components/ui/Tabs/TabNav'
|
||||||
import { NoteDto } from '@/proxy/note/models'
|
import { NoteDto } from '@/proxy/note/models'
|
||||||
import { AVATAR_URL } from '@/constants/app.constant'
|
import { AVATAR_URL } from '@/constants/app.constant'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
|
import apiService from '@/services/api.service'
|
||||||
|
import { PagedResultDto } from '@/proxy'
|
||||||
import { AuditLogActionDto, AuditLogDto } from '@/proxy/auditLog/audit-log'
|
import { AuditLogActionDto, AuditLogDto } from '@/proxy/auditLog/audit-log'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { auditLogService } from '@/services/auditLog.service'
|
|
||||||
|
|
||||||
interface NoteListProps {
|
interface NoteListProps {
|
||||||
notes: NoteDto[]
|
notes: NoteDto[]
|
||||||
entityName: string
|
entityName: string
|
||||||
entityId: string
|
entityId: string
|
||||||
isVisible?: boolean
|
|
||||||
onAddNote?: () => void
|
onAddNote?: () => void
|
||||||
onRefreshNotes?: () => void
|
|
||||||
onDeleteNote?: (noteId: string) => void
|
onDeleteNote?: (noteId: string) => void
|
||||||
onDownloadFile?: (fileData: any) => void
|
onDownloadFile?: (fileData: any) => void
|
||||||
}
|
}
|
||||||
|
|
@ -36,9 +35,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
notes,
|
notes,
|
||||||
entityName,
|
entityName,
|
||||||
entityId,
|
entityId,
|
||||||
isVisible = true,
|
|
||||||
onAddNote,
|
onAddNote,
|
||||||
onRefreshNotes,
|
|
||||||
onDeleteNote,
|
onDeleteNote,
|
||||||
onDownloadFile,
|
onDownloadFile,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -73,11 +70,6 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
icon: <FaEnvelope className="text-green-500" />,
|
icon: <FaEnvelope className="text-green-500" />,
|
||||||
border: 'border-green-400',
|
border: 'border-green-400',
|
||||||
}
|
}
|
||||||
case 'workflow':
|
|
||||||
return {
|
|
||||||
icon: <FaHistory className="text-purple-500" />,
|
|
||||||
border: 'border-purple-400',
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
icon: <FaStickyNote className="text-gray-400" />,
|
icon: <FaStickyNote className="text-gray-400" />,
|
||||||
|
|
@ -223,18 +215,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
const getRowLabelIfMatches = (input: any): string | null => {
|
const getRowLabelIfMatches = (input: any): string | null => {
|
||||||
if (!entityIdNormalized) return null
|
if (!entityIdNormalized) return null
|
||||||
const inputFormCode = normalize(input?.listFormCode)
|
const inputFormCode = normalize(input?.listFormCode)
|
||||||
if (!inputFormCode) return null
|
if (!inputFormCode || inputFormCode !== listFormCodeNormalized) return null
|
||||||
const data = input?.data
|
|
||||||
const isMainListForm = inputFormCode === listFormCodeNormalized
|
|
||||||
|
|
||||||
if (!isMainListForm) {
|
|
||||||
if (!data || typeof data !== 'object') return null
|
|
||||||
const hit = findMatchingValueInData(data, entityIdNormalized)
|
|
||||||
if (!hit) return null
|
|
||||||
|
|
||||||
const nameValue = (data as any)?.Name ?? (data as any)?.name
|
|
||||||
return nameValue ? String(nameValue) : String(hit.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = getKeysFromInput(input)
|
const keys = getKeysFromInput(input)
|
||||||
.map((k) => normalize(k))
|
.map((k) => normalize(k))
|
||||||
|
|
@ -246,6 +227,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some entities may use a different PK than the visible row key; allow strict match via input.data too.
|
// Some entities may use a different PK than the visible row key; allow strict match via input.data too.
|
||||||
|
const data = input?.data
|
||||||
if (data && typeof data === 'object') {
|
if (data && typeof data === 'object') {
|
||||||
const hit = findMatchingValueInData(data, entityIdNormalized)
|
const hit = findMatchingValueInData(data, entityIdNormalized)
|
||||||
if (hit) {
|
if (hit) {
|
||||||
|
|
@ -258,6 +240,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert: keys is null, match by scanning input.data for entity id/name/code/etc.
|
// insert: keys is null, match by scanning input.data for entity id/name/code/etc.
|
||||||
|
const data = input?.data
|
||||||
if (data && typeof data === 'object') {
|
if (data && typeof data === 'object') {
|
||||||
const hit = findMatchingValueInData(data, entityIdNormalized)
|
const hit = findMatchingValueInData(data, entityIdNormalized)
|
||||||
if (!hit) return null
|
if (!hit) return null
|
||||||
|
|
@ -294,15 +277,17 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
setAuditLoading(true)
|
setAuditLoading(true)
|
||||||
setAuditError(null)
|
setAuditError(null)
|
||||||
try {
|
try {
|
||||||
const response = await auditLogService.getList({
|
const response = await apiService.fetchData<PagedResultDto<AuditLogDto>>({
|
||||||
skipCount: 0,
|
method: 'GET',
|
||||||
maxResultCount: 200,
|
url: '/api/app/audit-log',
|
||||||
sorting: 'ExecutionTime DESC',
|
params: {
|
||||||
listFormCode: entityName,
|
skipCount: 0,
|
||||||
entityId,
|
maxResultCount: 200,
|
||||||
|
sorting: 'ExecutionTime DESC',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const items = response?.items ?? []
|
const items = response.data?.items ?? []
|
||||||
const filtered = items
|
const filtered = items
|
||||||
.map((log) => ({ log, matchedActions: buildMatchedActions(log) }))
|
.map((log) => ({ log, matchedActions: buildMatchedActions(log) }))
|
||||||
.filter((x) => x.matchedActions.length > 0)
|
.filter((x) => x.matchedActions.length > 0)
|
||||||
|
|
@ -320,13 +305,13 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isVisible) return
|
if (currentTab !== 'audit') return
|
||||||
if (!listFormCodeNormalized && !entityIdNormalized) return
|
if (!listFormCodeNormalized && !entityIdNormalized) return
|
||||||
const key = `${listFormCodeNormalized}|${entityIdNormalized}`
|
const key = `${listFormCodeNormalized}|${entityIdNormalized}`
|
||||||
if (auditLoadedKey === key) return
|
if (auditLoadedKey === key) return
|
||||||
loadAuditLogs()
|
loadAuditLogs()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isVisible, listFormCodeNormalized, entityIdNormalized])
|
}, [currentTab, listFormCodeNormalized, entityIdNormalized])
|
||||||
|
|
||||||
const getStatusBadge = (statusCode?: number) => {
|
const getStatusBadge = (statusCode?: number) => {
|
||||||
if (!statusCode) return <Badge className="bg-gray-500" content="?" />
|
if (!statusCode) return <Badge className="bg-gray-500" content="?" />
|
||||||
|
|
@ -345,7 +330,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
onChange={(val) => setCurrentTab(val as 'notes' | 'audit')}
|
onChange={(val) => setCurrentTab(val as 'notes' | 'audit')}
|
||||||
variant="underline"
|
variant="underline"
|
||||||
>
|
>
|
||||||
<TabList className="mb-2 border-0 dark:bg-gray-800">
|
<TabList className="mb-4 border-0 dark:bg-gray-800">
|
||||||
<TabNav value="notes">
|
<TabNav value="notes">
|
||||||
{translate('::ListForms.ListForm.Notes')}
|
{translate('::ListForms.ListForm.Notes')}
|
||||||
<Badge className="ml-2 bg-blue-500" content={`${notes?.length ?? 0}`} />
|
<Badge className="ml-2 bg-blue-500" content={`${notes?.length ?? 0}`} />
|
||||||
|
|
@ -356,48 +341,21 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
</TabNav>
|
</TabNav>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<div className="mb-2 flex min-h-10 items-center justify-end border-y border-gray-200 bg-gray-50 px-1 py-1 dark:border-gray-700 dark:bg-gray-900">
|
<TabContent value="notes">
|
||||||
{currentTab === 'notes' ? (
|
<div className="flex items-center justify-end mb-2">
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="xs"
|
|
||||||
icon={<FaPlus />}
|
|
||||||
type="button"
|
|
||||||
onClick={onAddNote}
|
|
||||||
disabled={!onAddNote}
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
{translate('::ListForms.ListForm.AddNote')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
variant="default"
|
|
||||||
type="button"
|
|
||||||
icon={<FaSyncAlt />}
|
|
||||||
onClick={onRefreshNotes}
|
|
||||||
disabled={!onRefreshNotes}
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
{translate('::ListForms.ListForm.Refresh')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
variant="default"
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
icon={<FaPlus className="mr-1" />}
|
||||||
type="button"
|
type="button"
|
||||||
icon={<FaSyncAlt />}
|
onClick={onAddNote}
|
||||||
onClick={loadAuditLogs}
|
disabled={!onAddNote}
|
||||||
disabled={auditLoading}
|
|
||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
{translate('::ListForms.ListForm.Refresh')}
|
{translate('::ListForms.ListForm.AddNote')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<TabContent value="notes">
|
|
||||||
{(notes?.length ?? 0) === 0 ? (
|
{(notes?.length ?? 0) === 0 ? (
|
||||||
<div className="flex flex-col items-center justify-center h-32 text-gray-500">
|
<div className="flex flex-col items-center justify-center h-32 text-gray-500">
|
||||||
<FaStickyNote className="text-4xl mb-2 opacity-50" />
|
<FaStickyNote className="text-4xl mb-2 opacity-50" />
|
||||||
|
|
@ -440,7 +398,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sil butonu */}
|
{/* Sil butonu */}
|
||||||
{user?.id === note.creatorId && note.type !== 'workflow' && (
|
{user?.id === note.creatorId && (
|
||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -491,6 +449,20 @@ export const NoteList: React.FC<NoteListProps> = ({
|
||||||
</TabContent>
|
</TabContent>
|
||||||
|
|
||||||
<TabContent value="audit">
|
<TabContent value="audit">
|
||||||
|
<div className="flex items-center justify-end mb-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="default"
|
||||||
|
type="button"
|
||||||
|
icon={<FaSyncAlt className="mr-1" />}
|
||||||
|
onClick={loadAuditLogs}
|
||||||
|
disabled={auditLoading}
|
||||||
|
className="flex items-center"
|
||||||
|
>
|
||||||
|
{translate('::ListForms.ListForm.Refresh')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{auditLoading ? (
|
{auditLoading ? (
|
||||||
<div className="flex items-center justify-center py-10">
|
<div className="flex items-center justify-center py-10">
|
||||||
<Spinner size={32} />
|
<Spinner size={32} />
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ function NoteModalContent({
|
||||||
const types = [
|
const types = [
|
||||||
{ value: 'note', label: translate('::ListForms.ListForm.NoteModal.Type.Note') },
|
{ value: 'note', label: translate('::ListForms.ListForm.NoteModal.Type.Note') },
|
||||||
{ value: 'message', label: translate('::ListForms.ListForm.NoteModal.Type.Message') },
|
{ value: 'message', label: translate('::ListForms.ListForm.NoteModal.Type.Message') },
|
||||||
|
{ value: 'activity', label: translate('::ListForms.ListForm.NoteModal.Type.Activity') },
|
||||||
]
|
]
|
||||||
|
|
||||||
const handleSave = async (values: any) => {
|
const handleSave = async (values: any) => {
|
||||||
|
|
@ -91,7 +92,7 @@ function NoteModalContent({
|
||||||
{/* Başlık */}
|
{/* Başlık */}
|
||||||
<div className="flex items-center justify-between mb-5 flex-shrink-0">
|
<div className="flex items-center justify-between mb-5 flex-shrink-0">
|
||||||
<h3 className="text-xl font-semibold flex items-center gap-3">
|
<h3 className="text-xl font-semibold flex items-center gap-3">
|
||||||
<div className="p-1 bg-purple-100 rounded-full">
|
<div className="p-2 bg-purple-100 rounded-full">
|
||||||
<FaPlus className="text-purple-600 text-lg" />
|
<FaPlus className="text-purple-600 text-lg" />
|
||||||
</div>
|
</div>
|
||||||
{translate('::ListForms.ListForm.AddNote')}
|
{translate('::ListForms.ListForm.AddNote')}
|
||||||
|
|
@ -115,7 +116,7 @@ function NoteModalContent({
|
||||||
{types.map((t) => (
|
{types.map((t) => (
|
||||||
<label
|
<label
|
||||||
key={t.value}
|
key={t.value}
|
||||||
className="flex items-center gap-2 px-1 py-1 text-black rounded-md cursor-pointer transition-all duration-200 dark:bg-gray-800 dark:text-gray-300"
|
className="flex items-center gap-2 px-2 py-1 text-black rounded-md cursor-pointer transition-all duration-200 dark:bg-gray-800 dark:text-gray-300"
|
||||||
>
|
>
|
||||||
<Radio
|
<Radio
|
||||||
value={t.value}
|
value={t.value}
|
||||||
|
|
@ -138,9 +139,8 @@ function NoteModalContent({
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
name="subject"
|
name="subject"
|
||||||
|
as={Input}
|
||||||
placeholder={translate('::ListForms.ListForm.NoteModal.Subject')}
|
placeholder={translate('::ListForms.ListForm.NoteModal.Subject')}
|
||||||
component={Input}
|
|
||||||
autoFocus
|
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ function NoteModalContent({
|
||||||
|
|
||||||
{/* DOSYA YÜKLEME */}
|
{/* DOSYA YÜKLEME */}
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-2 text-center hover:border-purple-400 transition-colors duration-200">
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-3 text-center hover:border-purple-400 transition-colors duration-200">
|
||||||
<Upload
|
<Upload
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
showList={false}
|
showList={false}
|
||||||
|
|
@ -255,7 +255,7 @@ function NoteModalContent({
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
||||||
{/* ALT BUTONLAR */}
|
{/* ALT BUTONLAR */}
|
||||||
<Dialog.Footer className="mt-2 flex justify-between items-center pt-4 border-t border-gray-200">
|
<Dialog.Footer className="mt-5 flex justify-between items-center pt-4 border-t border-gray-200">
|
||||||
<Button variant="default" size="md" onClick={onClose} disabled={uploading}>
|
<Button variant="default" size="md" onClick={onClose} disabled={uploading}>
|
||||||
{translate('::Cancel')}
|
{translate('::Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible) fetchActivities()
|
if (isVisible) fetchActivities()
|
||||||
}, [isVisible, entityName, entityId])
|
}, [isVisible])
|
||||||
|
|
||||||
const handleDownloadFile = async (fileData: any) => {
|
const handleDownloadFile = async (fileData: any) => {
|
||||||
if (!fileData?.SavedFileName) return
|
if (!fileData?.SavedFileName) return
|
||||||
|
|
@ -62,6 +62,8 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTotalCount = () => activities.length
|
||||||
|
|
||||||
// Draggable button handlers
|
// Draggable button handlers
|
||||||
const handleMouseDown = (e: React.MouseEvent) => {
|
const handleMouseDown = (e: React.MouseEvent) => {
|
||||||
if (!buttonRef.current) return
|
if (!buttonRef.current) return
|
||||||
|
|
@ -132,6 +134,7 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{isVisible ? <FaChevronRight /> : <FaChevronLeft />}
|
{isVisible ? <FaChevronRight /> : <FaChevronLeft />}
|
||||||
|
{getTotalCount() > 0 && <Badge content={getTotalCount()} />}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -184,9 +187,7 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
notes={activities}
|
notes={activities}
|
||||||
entityName={entityName}
|
entityName={entityName}
|
||||||
entityId={entityId}
|
entityId={entityId}
|
||||||
isVisible={isVisible}
|
|
||||||
onAddNote={() => setShowAddModal(true)}
|
onAddNote={() => setShowAddModal(true)}
|
||||||
onRefreshNotes={fetchActivities}
|
|
||||||
onDeleteNote={handleDeleteActivity}
|
onDeleteNote={handleDeleteActivity}
|
||||||
onDownloadFile={handleDownloadFile}
|
onDownloadFile={handleDownloadFile}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
style={{
|
style={{
|
||||||
touchAction: isDesignMode ? 'none' : 'pan-y',
|
touchAction: 'none',
|
||||||
transition:
|
transition:
|
||||||
'border-color 0.3s ease-out, opacity 0.3s ease-out, box-shadow 0.3s ease-out',
|
'border-color 0.3s ease-out, opacity 0.3s ease-out, box-shadow 0.3s ease-out',
|
||||||
willChange: isDragging ? 'opacity' : 'auto',
|
willChange: isDragging ? 'opacity' : 'auto',
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ import { useListFormColumns } from './useListFormColumns'
|
||||||
import { Loading } from '@/components/shared'
|
import { Loading } from '@/components/shared'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import { NotePanel } from '../form/notes/NotePanel'
|
|
||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -245,7 +244,7 @@ const Grid = (props: GridProps) => {
|
||||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { smaller } = useResponsive()
|
const { smaller } = useResponsive()
|
||||||
const currentUser = useStoreState((state) => state.auth.user)
|
const config = useStoreState((state) => state.abpConfig.config)
|
||||||
|
|
||||||
const gridRef = useRef<DataGridRef>()
|
const gridRef = useRef<DataGridRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
|
|
@ -263,10 +262,6 @@ const Grid = (props: GridProps) => {
|
||||||
const [gridDto, setGridDto] = useState<GridDto>()
|
const [gridDto, setGridDto] = useState<GridDto>()
|
||||||
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
||||||
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
||||||
const [notePanelTarget, setNotePanelTarget] = useState<{
|
|
||||||
entityName: string
|
|
||||||
entityId: string
|
|
||||||
} | null>(null)
|
|
||||||
|
|
||||||
type EditorOptionsWithButtons = {
|
type EditorOptionsWithButtons = {
|
||||||
buttons?: any[]
|
buttons?: any[]
|
||||||
|
|
@ -343,29 +338,11 @@ const Grid = (props: GridProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||||
|
|
||||||
const openNotePanel = useCallback(
|
|
||||||
(rowData: Record<string, any>) => {
|
|
||||||
const keyFieldName = gridDto?.gridOptions.keyFieldName
|
|
||||||
const entityId = getValueByField(rowData, keyFieldName)
|
|
||||||
if (entityId === undefined || entityId === null || entityId === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setNotePanelTarget({
|
|
||||||
entityName: gridDto?.gridOptions.listFormCode ?? listFormCode,
|
|
||||||
entityId: String(entityId),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[gridDto, listFormCode],
|
|
||||||
)
|
|
||||||
|
|
||||||
const { getBandedColumns } = useListFormColumns({
|
const { getBandedColumns } = useListFormColumns({
|
||||||
gridDto,
|
gridDto,
|
||||||
listFormCode,
|
listFormCode,
|
||||||
isSubForm,
|
isSubForm,
|
||||||
gridRef,
|
gridRef,
|
||||||
onShowNote: openNotePanel,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getSelectedRowKeys = useCallback(async () => {
|
const getSelectedRowKeys = useCallback(async () => {
|
||||||
|
|
@ -397,10 +374,10 @@ const Grid = (props: GridProps) => {
|
||||||
grd,
|
grd,
|
||||||
gridDto?.gridOptions.workflowDto,
|
gridDto?.gridOptions.workflowDto,
|
||||||
selectedRowsData ?? grd.getSelectedRowsData(),
|
selectedRowsData ?? grd.getSelectedRowsData(),
|
||||||
currentUser,
|
config?.currentUser,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[currentUser, gridDto],
|
[config?.currentUser, gridDto],
|
||||||
)
|
)
|
||||||
|
|
||||||
const refreshData = useCallback(() => {
|
const refreshData = useCallback(() => {
|
||||||
|
|
@ -1020,7 +997,7 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
// Kolonları oluştur - dil değiştiğinde güncelle
|
// Kolonları oluştur - dil değiştiğinde güncelle
|
||||||
const memoizedColumns = useMemo(() => {
|
const memoizedColumns = useMemo(() => {
|
||||||
if (!gridDto) return undefined
|
if (!gridDto || !config) return undefined
|
||||||
|
|
||||||
const cols = getBandedColumns()
|
const cols = getBandedColumns()
|
||||||
|
|
||||||
|
|
@ -1061,7 +1038,7 @@ const Grid = (props: GridProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return cols
|
return cols
|
||||||
}, [gridDto])
|
}, [gridDto, config])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setColumnData(memoizedColumns)
|
setColumnData(memoizedColumns)
|
||||||
|
|
@ -1474,27 +1451,7 @@ const Grid = (props: GridProps) => {
|
||||||
) {
|
) {
|
||||||
workflowService
|
workflowService
|
||||||
.startWorkflow(listFormCode, [insertedKey])
|
.startWorkflow(listFormCode, [insertedKey])
|
||||||
.then((result) => {
|
.then(() => gridRef.current?.instance()?.refresh())
|
||||||
const messages = result.toastMessages ?? []
|
|
||||||
if (messages.length > 0) {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="info" duration={7000}>
|
|
||||||
{messages.map((message, messageIndex) => (
|
|
||||||
<div
|
|
||||||
key={messageIndex}
|
|
||||||
className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}
|
|
||||||
>
|
|
||||||
{message.split('\n').map((line, lineIndex) => (
|
|
||||||
<div key={lineIndex}>{line}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'top-end' },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
gridRef.current?.instance()?.refresh()
|
|
||||||
})
|
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
@ -1917,14 +1874,6 @@ const Grid = (props: GridProps) => {
|
||||||
{toolbarModalData?.content}
|
{toolbarModalData?.content}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<GridFilterDialogs gridRef={gridRef} listFormCode={listFormCode} {...filterData} />
|
<GridFilterDialogs gridRef={gridRef} listFormCode={listFormCode} {...filterData} />
|
||||||
{notePanelTarget && (
|
|
||||||
<NotePanel
|
|
||||||
entityName={notePanelTarget.entityName}
|
|
||||||
entityId={notePanelTarget.entityId}
|
|
||||||
isVisible
|
|
||||||
onToggle={() => setNotePanelTarget(null)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ import { useStoreState } from '@/store/store'
|
||||||
import SubForms from '../form/SubForms'
|
import SubForms from '../form/SubForms'
|
||||||
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import { NotePanel } from '../form/notes/NotePanel'
|
|
||||||
|
|
||||||
interface TreeProps {
|
interface TreeProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -233,7 +232,6 @@ const Tree = (props: TreeProps) => {
|
||||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { smaller } = useResponsive()
|
const { smaller } = useResponsive()
|
||||||
const currentUser = useStoreState((state) => state.auth.user)
|
|
||||||
|
|
||||||
const gridRef = useRef<TreeListRef>()
|
const gridRef = useRef<TreeListRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
|
|
@ -251,12 +249,9 @@ const Tree = (props: TreeProps) => {
|
||||||
const [gridDto, setGridDto] = useState<GridDto>()
|
const [gridDto, setGridDto] = useState<GridDto>()
|
||||||
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
||||||
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
||||||
const [notePanelTarget, setNotePanelTarget] = useState<{
|
|
||||||
entityName: string
|
|
||||||
entityId: string
|
|
||||||
} | null>(null)
|
|
||||||
|
|
||||||
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
|
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
|
||||||
|
const config = useStoreState((state) => state.abpConfig.config)
|
||||||
|
|
||||||
type EditorOptionsWithButtons = {
|
type EditorOptionsWithButtons = {
|
||||||
buttons?: any[]
|
buttons?: any[]
|
||||||
|
|
@ -348,29 +343,11 @@ const Tree = (props: TreeProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||||
|
|
||||||
const openNotePanel = useCallback(
|
|
||||||
(rowData: Record<string, any>) => {
|
|
||||||
const keyFieldName = gridDto?.gridOptions.keyFieldName
|
|
||||||
const entityId = getValueByField(rowData, keyFieldName)
|
|
||||||
if (entityId === undefined || entityId === null || entityId === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setNotePanelTarget({
|
|
||||||
entityName: gridDto?.gridOptions.listFormCode ?? listFormCode,
|
|
||||||
entityId: String(entityId),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[gridDto, listFormCode],
|
|
||||||
)
|
|
||||||
|
|
||||||
const { getBandedColumns } = useListFormColumns({
|
const { getBandedColumns } = useListFormColumns({
|
||||||
gridDto,
|
gridDto,
|
||||||
listFormCode,
|
listFormCode,
|
||||||
isSubForm,
|
isSubForm,
|
||||||
gridRef,
|
gridRef,
|
||||||
onShowNote: openNotePanel,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const extractSearchParamsFields = useCallback((filter: any): [string, string, any][] => {
|
const extractSearchParamsFields = useCallback((filter: any): [string, string, any][] => {
|
||||||
|
|
@ -432,10 +409,10 @@ const Tree = (props: TreeProps) => {
|
||||||
tree,
|
tree,
|
||||||
gridDto?.gridOptions.workflowDto,
|
gridDto?.gridOptions.workflowDto,
|
||||||
selectedRowsData ?? tree.getSelectedRowsData(),
|
selectedRowsData ?? tree.getSelectedRowsData(),
|
||||||
currentUser,
|
config?.currentUser,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[currentUser, gridDto],
|
[config?.currentUser, gridDto],
|
||||||
)
|
)
|
||||||
|
|
||||||
const refreshData = useCallback(() => {
|
const refreshData = useCallback(() => {
|
||||||
|
|
@ -923,7 +900,7 @@ const Tree = (props: TreeProps) => {
|
||||||
}, [gridDto])
|
}, [gridDto])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gridDto) return
|
if (!gridDto || !config) return
|
||||||
|
|
||||||
const cols = getBandedColumns()
|
const cols = getBandedColumns()
|
||||||
setColumnData(cols)
|
setColumnData(cols)
|
||||||
|
|
@ -936,7 +913,7 @@ const Tree = (props: TreeProps) => {
|
||||||
cols,
|
cols,
|
||||||
)
|
)
|
||||||
setTreeListDataSource(dataSource)
|
setTreeListDataSource(dataSource)
|
||||||
}, [gridDto, searchParams])
|
}, [gridDto, searchParams, config])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const activeFilters = extraFilters.filter((f) => f.value)
|
const activeFilters = extraFilters.filter((f) => f.value)
|
||||||
|
|
@ -1133,27 +1110,7 @@ const Tree = (props: TreeProps) => {
|
||||||
) {
|
) {
|
||||||
workflowService
|
workflowService
|
||||||
.startWorkflow(listFormCode, [insertedKey])
|
.startWorkflow(listFormCode, [insertedKey])
|
||||||
.then((result) => {
|
.then(() => gridRef.current?.instance()?.refresh())
|
||||||
const messages = result.toastMessages ?? []
|
|
||||||
if (messages.length > 0) {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="info" duration={7000}>
|
|
||||||
{messages.map((message, messageIndex) => (
|
|
||||||
<div
|
|
||||||
key={messageIndex}
|
|
||||||
className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}
|
|
||||||
>
|
|
||||||
{message.split('\n').map((line, lineIndex) => (
|
|
||||||
<div key={lineIndex}>{line}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'top-end' },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
gridRef.current?.instance()?.refresh()
|
|
||||||
})
|
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
@ -1656,14 +1613,6 @@ const Tree = (props: TreeProps) => {
|
||||||
{toolbarModalData?.content}
|
{toolbarModalData?.content}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<GridFilterDialogs gridRef={gridRef as any} listFormCode={listFormCode} {...filterData} />
|
<GridFilterDialogs gridRef={gridRef as any} listFormCode={listFormCode} {...filterData} />
|
||||||
{notePanelTarget && (
|
|
||||||
<NotePanel
|
|
||||||
entityName={notePanelTarget.entityName}
|
|
||||||
entityId={notePanelTarget.entityId}
|
|
||||||
isVisible
|
|
||||||
onToggle={() => setNotePanelTarget(null)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -247,13 +247,11 @@ const useListFormColumns = ({
|
||||||
listFormCode,
|
listFormCode,
|
||||||
isSubForm,
|
isSubForm,
|
||||||
gridRef,
|
gridRef,
|
||||||
onShowNote,
|
|
||||||
}: {
|
}: {
|
||||||
gridDto?: GridDto
|
gridDto?: GridDto
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
isSubForm?: boolean
|
isSubForm?: boolean
|
||||||
gridRef?: any
|
gridRef?: any
|
||||||
onShowNote?: (rowData: Record<string, any>) => void
|
|
||||||
}) => {
|
}) => {
|
||||||
const dialog: any = useDialogContext()
|
const dialog: any = useDialogContext()
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -437,11 +435,6 @@ const useListFormColumns = ({
|
||||||
gridDto.gridOptions.editingOptionDto.allowDetail &&
|
gridDto.gridOptions.editingOptionDto.allowDetail &&
|
||||||
checkPermission(gridDto.gridOptions.permissionDto.u)
|
checkPermission(gridDto.gridOptions.permissionDto.u)
|
||||||
|
|
||||||
const hasShowNote =
|
|
||||||
gridDto.gridOptions.showNote &&
|
|
||||||
checkPermission(gridDto.gridOptions.permissionDto.n) &&
|
|
||||||
typeof onShowNote === 'function'
|
|
||||||
|
|
||||||
const hasDuplicate =
|
const hasDuplicate =
|
||||||
gridDto.gridOptions.editingOptionDto.allowDuplicate &&
|
gridDto.gridOptions.editingOptionDto.allowDuplicate &&
|
||||||
checkPermission(gridDto.gridOptions.permissionDto.i)
|
checkPermission(gridDto.gridOptions.permissionDto.i)
|
||||||
|
|
@ -449,15 +442,7 @@ const useListFormColumns = ({
|
||||||
const hasCommandButtons = gridDto.gridOptions.commandColumnDto.length > 0
|
const hasCommandButtons = gridDto.gridOptions.commandColumnDto.length > 0
|
||||||
|
|
||||||
// Eğer hiçbir buton eklenecek durumda değilse: çık
|
// Eğer hiçbir buton eklenecek durumda değilse: çık
|
||||||
if (
|
if (!hasUpdate && !hasDelete && !hasCreate && !hasCommandButtons) {
|
||||||
!hasUpdate &&
|
|
||||||
!hasDelete &&
|
|
||||||
!hasCreate &&
|
|
||||||
!hasDetail &&
|
|
||||||
!hasShowNote &&
|
|
||||||
!hasDuplicate &&
|
|
||||||
!hasCommandButtons
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,20 +485,6 @@ const useListFormColumns = ({
|
||||||
buttons.push(item)
|
buttons.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasShowNote) {
|
|
||||||
buttons.push({
|
|
||||||
name: 'note',
|
|
||||||
text: translate('::ListForms.ListForm.NoteModal.Type.Note'),
|
|
||||||
onClick: (e: any) => {
|
|
||||||
if (typeof e.event?.preventDefault === 'function') {
|
|
||||||
e.event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowNote?.(e.row.data)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasDuplicate) {
|
if (hasDuplicate) {
|
||||||
const item = {
|
const item = {
|
||||||
name: 'duplicate',
|
name: 'duplicate',
|
||||||
|
|
@ -629,7 +600,7 @@ const useListFormColumns = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return column as GridColumnData
|
return column as GridColumnData
|
||||||
}, [gridDto, checkPermission, translate, listFormCode, isPwaMode, dialog, gridRef, onShowNote])
|
}, [gridDto, checkPermission, translate, listFormCode, isPwaMode, dialog, gridRef])
|
||||||
|
|
||||||
const getColumns = useCallback(
|
const getColumns = useCallback(
|
||||||
(columnFormats: ColumnFormatDto[]) => {
|
(columnFormats: ColumnFormatDto[]) => {
|
||||||
|
|
|
||||||
|
|
@ -11,35 +11,12 @@ import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import type { WorkflowRunResultDto } from '@/services/workflow.service'
|
|
||||||
|
|
||||||
type ToolbarModalData = {
|
type ToolbarModalData = {
|
||||||
open: boolean
|
open: boolean
|
||||||
content?: JSX.Element
|
content?: JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
const showWorkflowToastMessages = (results: WorkflowRunResultDto | WorkflowRunResultDto[]) => {
|
|
||||||
const list = Array.isArray(results) ? results : [results]
|
|
||||||
const messages = list.flatMap((result) => result.toastMessages ?? [])
|
|
||||||
|
|
||||||
if (!messages.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.push(
|
|
||||||
<Notification type="info" duration={7000}>
|
|
||||||
{messages.map((message, messageIndex) => (
|
|
||||||
<div key={messageIndex} className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}>
|
|
||||||
{message.split('\n').map((line, lineIndex) => (
|
|
||||||
<div key={lineIndex}>{line}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'top-end' },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/
|
// https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/
|
||||||
// item.name > Accepted Values: 'addRowButton', 'applyFilterButton', 'columnChooserButton', 'exportButton', 'groupPanel', 'revertButton', 'saveButton', 'searchPanel'
|
// item.name > Accepted Values: 'addRowButton', 'applyFilterButton', 'columnChooserButton', 'exportButton', 'groupPanel', 'revertButton', 'saveButton', 'searchPanel'
|
||||||
const useToolbar = ({
|
const useToolbar = ({
|
||||||
|
|
@ -55,7 +32,7 @@ const useToolbar = ({
|
||||||
}: {
|
}: {
|
||||||
gridDto?: GridDto
|
gridDto?: GridDto
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
getSelectedRowKeys: () => unknown[] | Promise<unknown[]>
|
getSelectedRowKeys: () => void
|
||||||
getSelectedRowsData: () => any
|
getSelectedRowsData: () => any
|
||||||
refreshData: () => void
|
refreshData: () => void
|
||||||
getFilter: () => void
|
getFilter: () => void
|
||||||
|
|
@ -71,7 +48,7 @@ const useToolbar = ({
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { checkPermission } = usePermission()
|
const { checkPermission } = usePermission()
|
||||||
const isPwaMode = usePWA()
|
const isPwaMode = usePWA()
|
||||||
const currentUser = useStoreState((state) => state.auth.user)
|
const config = useStoreState((state) => state.abpConfig.config)
|
||||||
|
|
||||||
const [toolbarData, setToolbarData] = useState<ToolbarItem[]>([])
|
const [toolbarData, setToolbarData] = useState<ToolbarItem[]>([])
|
||||||
const [toolbarModalData, setToolbarModalData] = useState<ToolbarModalData>()
|
const [toolbarModalData, setToolbarModalData] = useState<ToolbarModalData>()
|
||||||
|
|
@ -136,8 +113,7 @@ const useToolbar = ({
|
||||||
})
|
})
|
||||||
|
|
||||||
const workflowOptions = grdOpt.workflowDto
|
const workflowOptions = grdOpt.workflowDto
|
||||||
const approvalCriteria =
|
const approvalCriteria = workflowOptions?.criteria?.filter((item) => item.kind === 'Approval') ?? []
|
||||||
workflowOptions?.criteria?.filter((item) => item.kind === 'Approval') ?? []
|
|
||||||
if (
|
if (
|
||||||
workflowOptions?.approvalStatusFieldName &&
|
workflowOptions?.approvalStatusFieldName &&
|
||||||
approvalCriteria.length > 0 &&
|
approvalCriteria.length > 0 &&
|
||||||
|
|
@ -173,7 +149,7 @@ const useToolbar = ({
|
||||||
) {
|
) {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="warning" duration={2500}>
|
<Notification type="warning" duration={2500}>
|
||||||
{translate('::WorkflowAlreadyStarted')}
|
Secili kayit icin workflow zaten baslamis.
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{ placement: 'top-end' },
|
{ placement: 'top-end' },
|
||||||
)
|
)
|
||||||
|
|
@ -181,8 +157,7 @@ const useToolbar = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await workflowService.startWorkflow(listFormCode, keys)
|
await workflowService.startWorkflow(listFormCode, keys)
|
||||||
showWorkflowToastMessages(result)
|
|
||||||
refreshData()
|
refreshData()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -229,14 +204,14 @@ const useToolbar = ({
|
||||||
row,
|
row,
|
||||||
workflowOptions,
|
workflowOptions,
|
||||||
criteria.title,
|
criteria.title,
|
||||||
getCurrentUserWorkflowIdentities(currentUser),
|
getCurrentUserWorkflowIdentities(config?.currentUser),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (activeRows.length !== selectedRows.length) {
|
if (activeRows.length !== selectedRows.length) {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="warning" duration={2500}>
|
<Notification type="warning" duration={2500}>
|
||||||
{translate('::SeciliKayitBekliyor')}
|
Secili kayit bu onay adiminda veya onay kullanicisinda beklemiyor.
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{ placement: 'top-end' },
|
{ placement: 'top-end' },
|
||||||
)
|
)
|
||||||
|
|
@ -327,75 +302,16 @@ const useToolbar = ({
|
||||||
text: translate('::ListForms.ListForm.DeleteSelectedRecords'),
|
text: translate('::ListForms.ListForm.DeleteSelectedRecords'),
|
||||||
icon: 'trash',
|
icon: 'trash',
|
||||||
visible: false,
|
visible: false,
|
||||||
async onClick() {
|
onClick() {
|
||||||
if (!grdOpt.deleteServiceAddress) {
|
if (!grdOpt.deleteServiceAddress) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedKeys = await Promise.resolve(getSelectedRowKeys())
|
dynamicFetch(grdOpt.deleteServiceAddress, 'POST', null, {
|
||||||
const keys = Array.isArray(selectedKeys) ? [...selectedKeys] : []
|
keys: getSelectedRowKeys(),
|
||||||
|
listFormCode,
|
||||||
if (!keys.length) {
|
}).then(() => {
|
||||||
toast.push(
|
refreshData()
|
||||||
<Notification type="warning" duration={2000}>
|
|
||||||
{translate('::ListForms.ListForm.SelectRecord')}
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'top-end' },
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setToolbarModalData({
|
|
||||||
open: true,
|
|
||||||
content: (
|
|
||||||
<>
|
|
||||||
<h5 className="mb-4">
|
|
||||||
{translate('::ListForms.ListForm.DeleteSelectedRecords')}
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{translate('::SeciliKayitlarSilmekIstiyormusunuz', {
|
|
||||||
0: keys.length,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="text-right mt-6">
|
|
||||||
<Button
|
|
||||||
className="ltr:mr-2 rtl:ml-2"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => setToolbarModalData(undefined)}
|
|
||||||
>
|
|
||||||
{translate('::Cancel')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="solid"
|
|
||||||
onClick={() => {
|
|
||||||
dynamicFetch(grdOpt.deleteServiceAddress!, 'POST', null, {
|
|
||||||
keys,
|
|
||||||
listFormCode,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
refreshData()
|
|
||||||
setToolbarModalData(undefined)
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="danger" duration={3000}>
|
|
||||||
{error?.response?.data?.error?.message ||
|
|
||||||
error?.response?.data?.message ||
|
|
||||||
error?.message ||
|
|
||||||
translate('::SilmeIslemiBasarisiz')}
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'top-end' },
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{translate('::Delete')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -426,13 +342,9 @@ const useToolbar = ({
|
||||||
open: true,
|
open: true,
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<h5 className="mb-4">{translate('::ListForms.ListForm.DeleteAllRecords')}</h5>
|
<h5 className="mb-4">Delete All Records</h5>
|
||||||
|
|
||||||
<p>
|
<p>Are you sure to delete all {r.data.totalCount} records?</p>
|
||||||
{translate('::TumKayitlariSilmekIstiyormusunuz', {
|
|
||||||
0: r.data.totalCount,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="text-right mt-6">
|
<div className="text-right mt-6">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -450,7 +362,7 @@ const useToolbar = ({
|
||||||
dynamicFetch('list-form-select/select', 'GET', parameters).then(() => {
|
dynamicFetch('list-form-select/select', 'GET', parameters).then(() => {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="success" duration={2000}>
|
<Notification type="success" duration={2000}>
|
||||||
{translate('::TumKayitlarSilindi')}
|
{'Tüm kayıtlar silindi.'}
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{
|
{
|
||||||
placement: 'top-end',
|
placement: 'top-end',
|
||||||
|
|
@ -571,9 +483,10 @@ const useToolbar = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gridDto && !listFormCode) return
|
if (!gridDto && !listFormCode) return
|
||||||
|
if (!config) return
|
||||||
|
|
||||||
getToolbarData()
|
getToolbarData()
|
||||||
}, [gridDto, listFormCode, currentUser])
|
}, [gridDto, listFormCode, config])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolbarData,
|
toolbarData,
|
||||||
|
|
@ -609,9 +522,7 @@ function isWorkflowApprovalCriteriaActive(
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeWorkflowValue(value: unknown) {
|
function normalizeWorkflowValue(value: unknown) {
|
||||||
return String(value ?? '')
|
return String(value ?? '').trim().toLocaleLowerCase('tr-TR')
|
||||||
.trim()
|
|
||||||
.toLocaleLowerCase('tr-TR')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWorkflowNotStarted(row: Record<string, unknown>, workflowOptions: WorkflowDto) {
|
function isWorkflowNotStarted(row: Record<string, unknown>, workflowOptions: WorkflowDto) {
|
||||||
|
|
@ -636,8 +547,7 @@ export function updateWorkflowApprovalToolbarItems(
|
||||||
name?: string
|
name?: string
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const approvalCriteria =
|
const approvalCriteria = workflowOptions?.criteria?.filter((item) => item.kind === 'Approval') ?? []
|
||||||
workflowOptions?.criteria?.filter((item) => item.kind === 'Approval') ?? []
|
|
||||||
if (!component || !workflowOptions?.approvalStatusFieldName || !approvalCriteria.length) {
|
if (!component || !workflowOptions?.approvalStatusFieldName || !approvalCriteria.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -676,12 +586,7 @@ export function updateWorkflowApprovalToolbarItems(
|
||||||
const enabled =
|
const enabled =
|
||||||
selectedRowsData.length > 0 &&
|
selectedRowsData.length > 0 &&
|
||||||
selectedRowsData.every((row) =>
|
selectedRowsData.every((row) =>
|
||||||
isWorkflowApprovalCriteriaActive(
|
isWorkflowApprovalCriteriaActive(row, workflowOptions, criteria.title, currentUserIdentities),
|
||||||
row,
|
|
||||||
workflowOptions,
|
|
||||||
criteria.title,
|
|
||||||
currentUserIdentities,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const optionPath = `toolbar.items[${toolbarItemIndex}].options.disabled`
|
const optionPath = `toolbar.items[${toolbarItemIndex}].options.disabled`
|
||||||
|
|
@ -714,12 +619,17 @@ function WorkflowApprovalDecisionDialog({
|
||||||
const decide = async (approved: boolean) => {
|
const decide = async (approved: boolean) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
try {
|
try {
|
||||||
const results = await Promise.all(
|
await Promise.all(
|
||||||
keys.map((key) =>
|
keys.map((key) =>
|
||||||
workflowService.decideWorkflow(listFormCode, [key], approved, note, criteriaId),
|
workflowService.decideWorkflow(
|
||||||
|
listFormCode,
|
||||||
|
[key],
|
||||||
|
approved,
|
||||||
|
note,
|
||||||
|
criteriaId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
showWorkflowToastMessages(results)
|
|
||||||
onCompleted()
|
onCompleted()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -739,33 +649,24 @@ function WorkflowApprovalDecisionDialog({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5 className="mb-4">{criteriaTitle}</h5>
|
<h5 className="mb-4">{criteriaTitle}</h5>
|
||||||
<p className="mb-4">
|
<p>{keys.length} kayit icin workflow karari verilecek.</p>
|
||||||
{translate('::App.Listform.ListformField.WorkflowDecisionMessage', {
|
<label className="mb-2 block font-semibold">Not</label>
|
||||||
0: keys.length,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
<textarea
|
<textarea
|
||||||
className="input input-textarea mb-4 min-h-[96px] w-full resize-y"
|
className="input input-textarea mb-4 min-h-[96px] w-full resize-y"
|
||||||
rows={4}
|
rows={4}
|
||||||
value={note}
|
value={note}
|
||||||
autoFocus
|
placeholder="Onay veya red aciklamasi"
|
||||||
placeholder={translate('::App.Listform.ListformField.ApprovalComment')}
|
|
||||||
onChange={(event) => setNote(event.target.value)}
|
onChange={(event) => setNote(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="text-right mt-6">
|
<div className="text-right mt-6">
|
||||||
<Button
|
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" disabled={submitting} onClick={onCancel}>
|
||||||
className="ltr:mr-2 rtl:ml-2"
|
|
||||||
variant="plain"
|
|
||||||
disabled={submitting}
|
|
||||||
onClick={onCancel}
|
|
||||||
>
|
|
||||||
{translate('::Cancel')}
|
{translate('::Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="ltr:mr-2 rtl:ml-2" disabled={submitting} onClick={() => decide(false)}>
|
<Button className="ltr:mr-2 rtl:ml-2" disabled={submitting} onClick={() => decide(false)}>
|
||||||
{translate('::App.Listform.ListformField.Rejecter')}
|
Reddet
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="solid" disabled={submitting} onClick={() => decide(true)}>
|
<Button variant="solid" disabled={submitting} onClick={() => decide(true)}>
|
||||||
{translate('::App.Listform.ListformField.Approver')}
|
Onayla
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -128,14 +128,7 @@ export interface MenuAddDialogProps {
|
||||||
initialParentCode: string
|
initialParentCode: string
|
||||||
initialOrder: number
|
initialOrder: number
|
||||||
rawItems: (MenuItem & { id?: string })[]
|
rawItems: (MenuItem & { id?: string })[]
|
||||||
onSaved: (menu: {
|
onSaved: () => void
|
||||||
code: string
|
|
||||||
parentCode?: string
|
|
||||||
menuTextEn: string
|
|
||||||
menuTextTr: string
|
|
||||||
icon?: string
|
|
||||||
shortName?: string
|
|
||||||
}) => void | Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MenuAddDialog({
|
export function MenuAddDialog({
|
||||||
|
|
@ -179,7 +172,7 @@ export function MenuAddDialog({
|
||||||
if (shortNameRequired && !form.shortName.trim()) return
|
if (shortNameRequired && !form.shortName.trim()) return
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
const savedMenu = {
|
await menuService.createWithLanguageKeyText({
|
||||||
code: form.code.trim(),
|
code: form.code.trim(),
|
||||||
displayName: form.code.trim(),
|
displayName: form.code.trim(),
|
||||||
parentCode: form.parentCode.trim() || undefined,
|
parentCode: form.parentCode.trim() || undefined,
|
||||||
|
|
@ -189,18 +182,9 @@ export function MenuAddDialog({
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
menuTextTr: form.menuTextTr.trim(),
|
menuTextTr: form.menuTextTr.trim(),
|
||||||
menuTextEn: form.menuTextEn.trim(),
|
menuTextEn: form.menuTextEn.trim(),
|
||||||
} as MenuDto
|
} as MenuDto)
|
||||||
|
|
||||||
await menuService.createWithLanguageKeyText(savedMenu)
|
onSaved()
|
||||||
|
|
||||||
await onSaved({
|
|
||||||
code: savedMenu.code!,
|
|
||||||
parentCode: savedMenu.parentCode,
|
|
||||||
menuTextEn: savedMenu.menuTextEn!,
|
|
||||||
menuTextTr: savedMenu.menuTextTr!,
|
|
||||||
icon: savedMenu.icon,
|
|
||||||
shortName: savedMenu.shortName,
|
|
||||||
})
|
|
||||||
onClose()
|
onClose()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
toast.push(<Notification title={e.message} type="danger" />, { placement: 'top-end' })
|
toast.push(<Notification title={e.message} type="danger" />, { placement: 'top-end' })
|
||||||
|
|
@ -358,11 +342,10 @@ export function MenuAddDialog({
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex justify-end gap-2 pt-1 border-t border-gray-100 dark:border-gray-700">
|
<div className="flex justify-end gap-2 pt-1 border-t border-gray-100 dark:border-gray-700">
|
||||||
<Button type="button" size="sm" variant="plain" onClick={onClose}>
|
<Button size="sm" variant="plain" onClick={onClose}>
|
||||||
{translate('::Cancel') || 'İptal'}
|
{translate('::Cancel') || 'İptal'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
loading={saving}
|
loading={saving}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue