CrudEndpoint ve CrudEndpointGenerate düzenlemesi
This commit is contained in:
parent
fe19cacedc
commit
2b96619a1d
22 changed files with 350 additions and 1848 deletions
|
|
@ -11,7 +11,6 @@ public class CrudEndpointDto : FullAuditedEntityDto<Guid>
|
||||||
public string OperationType { get; set; } = string.Empty;
|
public string OperationType { get; set; } = string.Empty;
|
||||||
public string CsharpCode { get; set; } = string.Empty;
|
public string CsharpCode { get; set; } = string.Empty;
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
public Guid EntityId { get; set; }
|
|
||||||
|
|
||||||
// Navigation property for display purposes
|
// Navigation property for display purposes
|
||||||
public string? EntityDisplayName { get; set; }
|
public string? EntityDisplayName { get; set; }
|
||||||
|
|
@ -25,6 +24,5 @@ public class CreateUpdateCrudEndpointDto
|
||||||
public string OperationType { get; set; } = string.Empty;
|
public string OperationType { get; set; } = string.Empty;
|
||||||
public string CsharpCode { get; set; } = string.Empty;
|
public string CsharpCode { get; set; } = string.Empty;
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
public Guid EntityId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ public interface ICrudEndpointAppService : ICrudAppService<
|
||||||
CreateUpdateCrudEndpointDto>
|
CreateUpdateCrudEndpointDto>
|
||||||
{
|
{
|
||||||
Task<List<CrudEndpointDto>> GetActiveEndpointsAsync();
|
Task<List<CrudEndpointDto>> GetActiveEndpointsAsync();
|
||||||
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(Guid entityId);
|
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
||||||
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(Guid entityId);
|
Task<CrudEndpointDto> ToggleAsync(Guid id);
|
||||||
|
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
|
||||||
using Volo.Abp.Application.Services;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.DeveloperKit;
|
|
||||||
|
|
||||||
public interface ISqlTableAppService : ICrudAppService<
|
|
||||||
SqlTableDto,
|
|
||||||
Guid,
|
|
||||||
PagedAndSortedResultRequestDto,
|
|
||||||
CreateUpdateSqlTableDto>
|
|
||||||
{
|
|
||||||
Task<List<SqlTableDto>> GetActiveEntitiesAsync();
|
|
||||||
Task<SqlTableDto> ToggleActiveStatusAsync(Guid id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.DeveloperKit;
|
|
||||||
|
|
||||||
public class SqlTableDto : FullAuditedEntityDto<Guid>
|
|
||||||
{
|
|
||||||
public string Menu { get; set; } = string.Empty;
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
|
||||||
public string TableName { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
public bool IsFullAuditedEntity { get; set; } = true;
|
|
||||||
public bool IsMultiTenant { get; set; } = false;
|
|
||||||
public string EndpointStatus { get; set; } = "pending";
|
|
||||||
|
|
||||||
public List<SqlTableFieldDto> Fields { get; set; } = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateUpdateSqlTableDto
|
|
||||||
{
|
|
||||||
public string Menu { get; set; } = string.Empty;
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
|
||||||
public string TableName { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
public bool IsFullAuditedEntity { get; set; } = true;
|
|
||||||
public bool IsMultiTenant { get; set; } = false;
|
|
||||||
|
|
||||||
public List<CreateUpdateSqlTableFieldDto> Fields { get; set; } = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SqlTableFieldDto : FullAuditedEntityDto<Guid>
|
|
||||||
{
|
|
||||||
public Guid EntityId { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Type { get; set; } = string.Empty;
|
|
||||||
public bool IsRequired { get; set; }
|
|
||||||
public int? MaxLength { get; set; }
|
|
||||||
public bool IsUnique { get; set; }
|
|
||||||
public string? DefaultValue { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public int DisplayOrder { get; set; } = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateUpdateSqlTableFieldDto
|
|
||||||
{
|
|
||||||
public Guid? Id { get; set; }
|
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Type { get; set; } = string.Empty;
|
|
||||||
public bool IsRequired { get; set; }
|
|
||||||
public int? MaxLength { get; set; }
|
|
||||||
public bool IsUnique { get; set; }
|
|
||||||
public string? DefaultValue { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public int DisplayOrder { get; set; } = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -3,15 +3,15 @@ using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Platform.DeveloperKit;
|
using Sozsoft.Platform.DeveloperKit;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Sozsoft.Platform;
|
||||||
|
|
||||||
namespace Platform.Api.Application;
|
namespace Platform.Api.Application;
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
public class CrudEndpointGenerateAppService : CrudAppService<
|
public class CrudEndpointGenerateAppService : CrudAppService<
|
||||||
CrudEndpoint,
|
CrudEndpoint,
|
||||||
CrudEndpointDto,
|
CrudEndpointDto,
|
||||||
|
|
@ -19,116 +19,95 @@ public class CrudEndpointGenerateAppService : CrudAppService<
|
||||||
PagedAndSortedResultRequestDto,
|
PagedAndSortedResultRequestDto,
|
||||||
CreateUpdateCrudEndpointDto>, ICrudEndpointAppService
|
CreateUpdateCrudEndpointDto>, ICrudEndpointAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<SqlTable, Guid> _entityRepository;
|
|
||||||
private readonly IRepository<CrudEndpoint, Guid> _endpointRepository;
|
|
||||||
|
|
||||||
public CrudEndpointGenerateAppService(
|
public CrudEndpointGenerateAppService(
|
||||||
IRepository<CrudEndpoint, Guid> repository,
|
IRepository<CrudEndpoint, Guid> repository)
|
||||||
IRepository<SqlTable, Guid> entityRepository,
|
|
||||||
IRepository<CrudEndpoint, Guid> endpointRepository)
|
|
||||||
: base(repository)
|
: base(repository)
|
||||||
{
|
{
|
||||||
_entityRepository = entityRepository;
|
|
||||||
_endpointRepository = endpointRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("api/app/crud-endpoint-generate/active-endpoints")]
|
||||||
|
[Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)]
|
||||||
public virtual async Task<List<CrudEndpointDto>> GetActiveEndpointsAsync()
|
public virtual async Task<List<CrudEndpointDto>> GetActiveEndpointsAsync()
|
||||||
{
|
{
|
||||||
var endpoints = await Repository.GetListAsync(x => x.IsActive);
|
var endpoints = await Repository.GetListAsync(x => x.IsActive);
|
||||||
return await MapToGetListOutputDtosAsync(endpoints);
|
return await MapToGetListOutputDtosAsync(endpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(Guid entityId)
|
[HttpGet("api/app/crud-endpoint-generate/endpoints-by-entity/{entityName}")]
|
||||||
|
[Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)]
|
||||||
|
public virtual async Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName)
|
||||||
{
|
{
|
||||||
var endpoints = await _endpointRepository.GetListAsync(x => x.EntityId == entityId);
|
var endpoints = await Repository.GetListAsync(x => x.EntityName == entityName);
|
||||||
return ObjectMapper.Map<List<CrudEndpoint>, List<CrudEndpointDto>>(endpoints);
|
return ObjectMapper.Map<List<CrudEndpoint>, List<CrudEndpointDto>>(endpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(Guid entityId)
|
[HttpPost("api/app/crud-endpoint-generate/{id}/toggle")]
|
||||||
|
[Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)]
|
||||||
|
public virtual async Task<CrudEndpointDto> ToggleAsync(Guid id)
|
||||||
{
|
{
|
||||||
// Entity + Fields
|
var endpoint = await Repository.GetAsync(id);
|
||||||
var entityQueryable = await _entityRepository.GetQueryableAsync();
|
endpoint.IsActive = !endpoint.IsActive;
|
||||||
var entity = await entityQueryable
|
await Repository.UpdateAsync(endpoint, autoSave: true);
|
||||||
.Include(x => x.Fields)
|
return ObjectMapper.Map<CrudEndpoint, CrudEndpointDto>(endpoint);
|
||||||
.FirstOrDefaultAsync(x => x.Id == entityId);
|
}
|
||||||
|
|
||||||
if (entity == null)
|
|
||||||
{
|
|
||||||
throw new Exception($"Entity with ID {entityId} not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
[HttpPost("api/app/crud-endpoint-generate/generate-crud-endpoints/{entityName}")]
|
||||||
|
[Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)]
|
||||||
|
public virtual async Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName)
|
||||||
|
{
|
||||||
// CRUD endpointleri oluştur
|
// CRUD endpointleri oluştur
|
||||||
var endpoints = new List<CrudEndpoint>();
|
var endpoints = new List<CrudEndpoint>
|
||||||
var entityName = entity.Name;
|
|
||||||
var entityDisplayName = entity.DisplayName;
|
|
||||||
|
|
||||||
endpoints.Add(new CrudEndpoint
|
|
||||||
{
|
{
|
||||||
EntityId = entityId,
|
new() {
|
||||||
EntityName = entityName,
|
EntityName = entityName,
|
||||||
Method = "GET",
|
Method = "GET",
|
||||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}",
|
Path = $"/api/app/crudendpoint/{entityName}",
|
||||||
OperationType = "GetList",
|
OperationType = "GetList",
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
CsharpCode = GenerateGetAllCode(entityName, entityDisplayName)
|
CsharpCode = GenerateGetAllCode(entityName)
|
||||||
});
|
},
|
||||||
|
new() {
|
||||||
endpoints.Add(new CrudEndpoint
|
EntityName = entityName,
|
||||||
{
|
Method = "GET",
|
||||||
EntityId = entityId,
|
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||||
EntityName = entityName,
|
OperationType = "GetById",
|
||||||
Method = "GET",
|
IsActive = true,
|
||||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}",
|
CsharpCode = GenerateGetByIdCode(entityName)
|
||||||
OperationType = "GetById",
|
},
|
||||||
IsActive = true,
|
new() {
|
||||||
CsharpCode = GenerateGetByIdCode(entityName, entityDisplayName)
|
EntityName = entityName,
|
||||||
});
|
Method = "POST",
|
||||||
|
Path = $"/api/app/crudendpoint/{entityName}",
|
||||||
endpoints.Add(new CrudEndpoint
|
OperationType = "Create",
|
||||||
{
|
IsActive = true,
|
||||||
EntityId = entityId,
|
CsharpCode = GenerateCreateCode(entityName)
|
||||||
EntityName = entityName,
|
},
|
||||||
Method = "POST",
|
new() {
|
||||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}",
|
EntityName = entityName,
|
||||||
OperationType = "Create",
|
Method = "PUT",
|
||||||
IsActive = true,
|
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||||
CsharpCode = GenerateCreateCode(entityName, entityDisplayName)
|
OperationType = "Update",
|
||||||
});
|
IsActive = true,
|
||||||
|
CsharpCode = GenerateUpdateCode(entityName)
|
||||||
endpoints.Add(new CrudEndpoint
|
},
|
||||||
{
|
new() {
|
||||||
EntityId = entityId,
|
EntityName = entityName,
|
||||||
EntityName = entityName,
|
Method = "DELETE",
|
||||||
Method = "PUT",
|
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}",
|
OperationType = "Delete",
|
||||||
OperationType = "Update",
|
IsActive = true,
|
||||||
IsActive = true,
|
CsharpCode = GenerateDeleteCode(entityName)
|
||||||
CsharpCode = GenerateUpdateCode(entityName, entityDisplayName)
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
endpoints.Add(new CrudEndpoint
|
|
||||||
{
|
|
||||||
EntityId = entityId,
|
|
||||||
EntityName = entityName,
|
|
||||||
Method = "DELETE",
|
|
||||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}",
|
|
||||||
OperationType = "Delete",
|
|
||||||
IsActive = true,
|
|
||||||
CsharpCode = GenerateDeleteCode(entityName, entityDisplayName)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Var olanları sil
|
// Var olanları sil
|
||||||
var existingEndpoints = await _endpointRepository
|
var existingEndpoints = await Repository
|
||||||
.GetListAsync(x => x.EntityId == entityId);
|
.GetListAsync(x => x.EntityName == entityName);
|
||||||
|
|
||||||
await _endpointRepository.DeleteManyAsync(existingEndpoints);
|
await Repository.DeleteManyAsync(existingEndpoints);
|
||||||
|
|
||||||
// Yeni endpointleri ekle
|
// Yeni endpointleri ekle
|
||||||
await _endpointRepository.InsertManyAsync(endpoints, autoSave: true);
|
await Repository.InsertManyAsync(endpoints, autoSave: true);
|
||||||
|
|
||||||
// Entity endpoint durumu güncelle
|
|
||||||
entity.EndpointStatus = "Uygulandı";
|
|
||||||
await _entityRepository.UpdateAsync(entity, autoSave: true);
|
|
||||||
|
|
||||||
var result = ObjectMapper.Map<List<CrudEndpoint>, List<CrudEndpointDto>>(endpoints);
|
var result = ObjectMapper.Map<List<CrudEndpoint>, List<CrudEndpointDto>>(endpoints);
|
||||||
|
|
||||||
|
|
@ -139,7 +118,7 @@ public class CrudEndpointGenerateAppService : CrudAppService<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateGetAllCode(string entityName, string displayName)
|
private string GenerateGetAllCode(string entityName)
|
||||||
{
|
{
|
||||||
return $@"[HttpGet]
|
return $@"[HttpGet]
|
||||||
public async Task<ActionResult<List<{entityName}>>> GetAll{entityName}sAsync()
|
public async Task<ActionResult<List<{entityName}>>> GetAll{entityName}sAsync()
|
||||||
|
|
@ -149,7 +128,7 @@ public async Task<ActionResult<List<{entityName}>>> GetAll{entityName}sAsync()
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateGetByIdCode(string entityName, string displayName)
|
private string GenerateGetByIdCode(string entityName)
|
||||||
{
|
{
|
||||||
return $@"[HttpGet(""{{id}}"")]
|
return $@"[HttpGet(""{{id}}"")]
|
||||||
public async Task<ActionResult<{entityName}>> Get{entityName}Async(Guid id)
|
public async Task<ActionResult<{entityName}>> Get{entityName}Async(Guid id)
|
||||||
|
|
@ -157,13 +136,13 @@ public async Task<ActionResult<{entityName}>> Get{entityName}Async(Guid id)
|
||||||
var entity = await _context.{entityName}s.FindAsync(id);
|
var entity = await _context.{entityName}s.FindAsync(id);
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{{
|
{{
|
||||||
return NotFound($""{displayName} with ID {{id}} not found"");
|
return NotFound($""{entityName} with ID {{id}} not found"");
|
||||||
}}
|
}}
|
||||||
return Ok(entity);
|
return Ok(entity);
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateCreateCode(string entityName, string displayName)
|
private string GenerateCreateCode(string entityName)
|
||||||
{
|
{
|
||||||
return $@"[HttpPost]
|
return $@"[HttpPost]
|
||||||
public async Task<ActionResult<{entityName}>> Create{entityName}Async({entityName} {entityName.ToLower()})
|
public async Task<ActionResult<{entityName}>> Create{entityName}Async({entityName} {entityName.ToLower()})
|
||||||
|
|
@ -174,7 +153,7 @@ public async Task<ActionResult<{entityName}>> Create{entityName}Async({entityNam
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateUpdateCode(string entityName, string displayName)
|
private string GenerateUpdateCode(string entityName)
|
||||||
{
|
{
|
||||||
return $@"[HttpPut(""{{id}}"")]
|
return $@"[HttpPut(""{{id}}"")]
|
||||||
public async Task<IActionResult> Update{entityName}Async(Guid id, {entityName} {entityName.ToLower()})
|
public async Task<IActionResult> Update{entityName}Async(Guid id, {entityName} {entityName.ToLower()})
|
||||||
|
|
@ -194,7 +173,7 @@ public async Task<IActionResult> Update{entityName}Async(Guid id, {entityName} {
|
||||||
{{
|
{{
|
||||||
if (!await {entityName}ExistsAsync(id))
|
if (!await {entityName}ExistsAsync(id))
|
||||||
{{
|
{{
|
||||||
return NotFound($""{displayName} with ID {{id}} not found"");
|
return NotFound($""{entityName} with ID {{id}} not found"");
|
||||||
}}
|
}}
|
||||||
throw;
|
throw;
|
||||||
}}
|
}}
|
||||||
|
|
@ -203,7 +182,7 @@ public async Task<IActionResult> Update{entityName}Async(Guid id, {entityName} {
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateDeleteCode(string entityName, string displayName)
|
private string GenerateDeleteCode(string entityName)
|
||||||
{
|
{
|
||||||
return $@"[HttpDelete(""{{id}}"")]
|
return $@"[HttpDelete(""{{id}}"")]
|
||||||
public async Task<IActionResult> Delete{entityName}Async(Guid id)
|
public async Task<IActionResult> Delete{entityName}Async(Guid id)
|
||||||
|
|
@ -211,7 +190,7 @@ public async Task<IActionResult> Delete{entityName}Async(Guid id)
|
||||||
var {entityName.ToLower()} = await _context.{entityName}s.FindAsync(id);
|
var {entityName.ToLower()} = await _context.{entityName}s.FindAsync(id);
|
||||||
if ({entityName.ToLower()} == null)
|
if ({entityName.ToLower()} == null)
|
||||||
{{
|
{{
|
||||||
return NotFound($""{displayName} with ID {{id}} not found"");
|
return NotFound($""{entityName} with ID {{id}} not found"");
|
||||||
}}
|
}}
|
||||||
|
|
||||||
_context.{entityName}s.Remove({entityName.ToLower()});
|
_context.{entityName}s.Remove({entityName.ToLower()});
|
||||||
|
|
@ -226,5 +205,3 @@ private async Task<bool> {entityName}ExistsAsync(Guid id)
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,12 @@ public class DeveloperKitAutoMapperProfile : Profile
|
||||||
{
|
{
|
||||||
public DeveloperKitAutoMapperProfile()
|
public DeveloperKitAutoMapperProfile()
|
||||||
{
|
{
|
||||||
// SqlTable mappings
|
|
||||||
CreateMap<SqlTable, SqlTableDto>();
|
|
||||||
CreateMap<CreateUpdateSqlTableDto, SqlTable>();
|
|
||||||
|
|
||||||
// EntityField mappings
|
|
||||||
CreateMap<SqlTableField, SqlTableFieldDto>();
|
|
||||||
CreateMap<CreateUpdateSqlTableFieldDto, SqlTableField>();
|
|
||||||
|
|
||||||
// CustomComponent mappings
|
// CustomComponent mappings
|
||||||
CreateMap<CustomComponent, CustomComponentDto>();
|
CreateMap<CustomComponent, CustomComponentDto>();
|
||||||
CreateMap<CreateUpdateCustomComponentDto, CustomComponent>();
|
CreateMap<CreateUpdateCustomComponentDto, CustomComponent>();
|
||||||
|
|
||||||
// GeneratedEndpoint mappings
|
// GeneratedEndpoint mappings
|
||||||
CreateMap<CrudEndpoint, CrudEndpointDto>()
|
CreateMap<CrudEndpoint, CrudEndpointDto>();
|
||||||
.ForMember(dest => dest.EntityDisplayName, opt => opt.MapFrom(src => src.Entity != null ? src.Entity.DisplayName : null));
|
|
||||||
CreateMap<CreateUpdateCrudEndpointDto, CrudEndpoint>();
|
CreateMap<CreateUpdateCrudEndpointDto, CrudEndpoint>();
|
||||||
|
|
||||||
CreateMap<DynamicService, DynamicServiceDto>()
|
CreateMap<DynamicService, DynamicServiceDto>()
|
||||||
|
|
|
||||||
|
|
@ -1,240 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
|
||||||
using Volo.Abp.Application.Services;
|
|
||||||
using Volo.Abp.Domain.Repositories;
|
|
||||||
using Volo.Abp.Domain.Entities;
|
|
||||||
using Sozsoft.Platform.Entities;
|
|
||||||
using Sozsoft.Platform.DeveloperKit;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
|
|
||||||
namespace Platform.Api.Application;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
public class SqlTableAppService : CrudAppService<
|
|
||||||
SqlTable,
|
|
||||||
SqlTableDto,
|
|
||||||
Guid,
|
|
||||||
PagedAndSortedResultRequestDto,
|
|
||||||
CreateUpdateSqlTableDto>, ISqlTableAppService
|
|
||||||
{
|
|
||||||
private readonly IRepository<SqlTable, Guid> _repository;
|
|
||||||
private readonly IRepository<SqlTableField, Guid> _fieldRepository;
|
|
||||||
private readonly IRepository<CrudEndpoint, Guid> _endpointRepository;
|
|
||||||
|
|
||||||
public SqlTableAppService(
|
|
||||||
IRepository<SqlTable, Guid> repository,
|
|
||||||
IRepository<SqlTableField, Guid> fieldRepository,
|
|
||||||
IRepository<CrudEndpoint, Guid> endpointRepository) : base(repository)
|
|
||||||
{
|
|
||||||
_repository = repository;
|
|
||||||
_fieldRepository = fieldRepository;
|
|
||||||
_endpointRepository = endpointRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<PagedResultDto<SqlTableDto>> GetListAsync(PagedAndSortedResultRequestDto input)
|
|
||||||
{
|
|
||||||
var query = await _repository.GetQueryableAsync();
|
|
||||||
var fullQuery = query.Include(x => x.Fields.OrderBy(f => f.DisplayOrder));
|
|
||||||
|
|
||||||
var totalCount = await fullQuery.CountAsync();
|
|
||||||
|
|
||||||
var entities = await fullQuery
|
|
||||||
.OrderBy(x => x.CreationTime)
|
|
||||||
.Skip(input.SkipCount)
|
|
||||||
.Take(input.MaxResultCount)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var dtos = ObjectMapper.Map<List<SqlTable>, List<SqlTableDto>>(entities);
|
|
||||||
|
|
||||||
return new PagedResultDto<SqlTableDto>(totalCount, dtos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<SqlTableDto> GetAsync(Guid id)
|
|
||||||
{
|
|
||||||
var query = await _repository.GetQueryableAsync();
|
|
||||||
var entity = await query
|
|
||||||
.Include(x => x.Fields.OrderBy(f => f.DisplayOrder))
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (entity == null)
|
|
||||||
throw new EntityNotFoundException($"Sql Table with id {id} not found");
|
|
||||||
|
|
||||||
return ObjectMapper.Map<SqlTable, SqlTableDto>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<SqlTableDto>> GetActiveEntitiesAsync()
|
|
||||||
{
|
|
||||||
var query = await _repository.GetQueryableAsync();
|
|
||||||
var entities = await query
|
|
||||||
.Include(x => x.Fields.OrderBy(f => f.DisplayOrder))
|
|
||||||
.Where(x => x.IsActive)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return ObjectMapper.Map<List<SqlTable>, List<SqlTableDto>>(entities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SqlTableDto> ToggleActiveStatusAsync(Guid id)
|
|
||||||
{
|
|
||||||
var query = await _repository.GetQueryableAsync();
|
|
||||||
var entity = await query
|
|
||||||
.Include(x => x.Fields.OrderBy(f => f.DisplayOrder))
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (entity == null)
|
|
||||||
throw new EntityNotFoundException($"Sql Table with id {id} not found");
|
|
||||||
|
|
||||||
entity.IsActive = !entity.IsActive;
|
|
||||||
await _repository.UpdateAsync(entity, autoSave: true);
|
|
||||||
|
|
||||||
return ObjectMapper.Map<SqlTable, SqlTableDto>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<SqlTableDto> UpdateAsync(Guid id, CreateUpdateSqlTableDto input)
|
|
||||||
{
|
|
||||||
var query = await _repository.GetQueryableAsync();
|
|
||||||
var entity = await query
|
|
||||||
.Include(x => x.Fields)
|
|
||||||
.FirstOrDefaultAsync(e => e.Id == id);
|
|
||||||
|
|
||||||
if (entity == null)
|
|
||||||
throw new EntityNotFoundException(typeof(SqlTable), id);
|
|
||||||
|
|
||||||
entity.Menu = input.Menu;
|
|
||||||
entity.Name = input.Name;
|
|
||||||
entity.DisplayName = input.DisplayName;
|
|
||||||
entity.TableName = input.TableName;
|
|
||||||
entity.Description = input.Description;
|
|
||||||
entity.IsActive = input.IsActive;
|
|
||||||
entity.IsFullAuditedEntity = input.IsFullAuditedEntity;
|
|
||||||
entity.IsMultiTenant = input.IsMultiTenant;
|
|
||||||
|
|
||||||
var updatedFields = new List<SqlTableField>();
|
|
||||||
|
|
||||||
for (int i = 0; i < input.Fields.Count; i++)
|
|
||||||
{
|
|
||||||
var dtoField = input.Fields[i];
|
|
||||||
SqlTableField? existingField = null;
|
|
||||||
|
|
||||||
if (dtoField.Id.HasValue)
|
|
||||||
{
|
|
||||||
existingField = entity.Fields?.FirstOrDefault(f => f.Id == dtoField.Id.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingField != null)
|
|
||||||
{
|
|
||||||
existingField.Name = dtoField.Name;
|
|
||||||
existingField.Type = dtoField.Type;
|
|
||||||
existingField.IsRequired = dtoField.IsRequired;
|
|
||||||
existingField.MaxLength = dtoField.MaxLength;
|
|
||||||
existingField.IsUnique = dtoField.IsUnique;
|
|
||||||
existingField.DefaultValue = dtoField.DefaultValue;
|
|
||||||
existingField.Description = dtoField.Description;
|
|
||||||
existingField.DisplayOrder = dtoField.DisplayOrder;
|
|
||||||
|
|
||||||
updatedFields.Add(existingField);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var newField = new SqlTableField
|
|
||||||
{
|
|
||||||
EntityId = entity.Id,
|
|
||||||
Name = dtoField.Name,
|
|
||||||
Type = dtoField.Type,
|
|
||||||
IsRequired = dtoField.IsRequired,
|
|
||||||
MaxLength = dtoField.MaxLength,
|
|
||||||
IsUnique = dtoField.IsUnique,
|
|
||||||
DefaultValue = dtoField.DefaultValue,
|
|
||||||
Description = dtoField.Description,
|
|
||||||
DisplayOrder = dtoField.DisplayOrder
|
|
||||||
};
|
|
||||||
|
|
||||||
await _fieldRepository.InsertAsync(newField);
|
|
||||||
updatedFields.Add(newField);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Silinecek alanlar
|
|
||||||
var toRemove = entity.Fields?
|
|
||||||
.Where(existing => updatedFields.All(f => f.Id != existing.Id))
|
|
||||||
.ToList() ?? [];
|
|
||||||
|
|
||||||
if (toRemove.Any())
|
|
||||||
{
|
|
||||||
await _fieldRepository.DeleteManyAsync(toRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _repository.UpdateAsync(entity, autoSave: true);
|
|
||||||
|
|
||||||
return ObjectMapper.Map<SqlTable, SqlTableDto>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<SqlTableDto> CreateAsync(CreateUpdateSqlTableDto input)
|
|
||||||
{
|
|
||||||
// Entity oluştur
|
|
||||||
var entity = new SqlTable
|
|
||||||
{
|
|
||||||
Menu = input.Menu,
|
|
||||||
Name = input.Name,
|
|
||||||
DisplayName = input.DisplayName,
|
|
||||||
TableName = input.TableName,
|
|
||||||
Description = input.Description,
|
|
||||||
IsActive = input.IsActive,
|
|
||||||
IsFullAuditedEntity = input.IsFullAuditedEntity,
|
|
||||||
IsMultiTenant = input.IsMultiTenant,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fields ekle - sıralama ile
|
|
||||||
for (int i = 0; i < input.Fields.Count; i++)
|
|
||||||
{
|
|
||||||
var fieldDto = input.Fields[i];
|
|
||||||
var field = new SqlTableField
|
|
||||||
{
|
|
||||||
EntityId = entity.Id,
|
|
||||||
Name = fieldDto.Name,
|
|
||||||
Type = fieldDto.Type,
|
|
||||||
IsRequired = fieldDto.IsRequired,
|
|
||||||
MaxLength = fieldDto.MaxLength,
|
|
||||||
IsUnique = fieldDto.IsUnique,
|
|
||||||
DefaultValue = fieldDto.DefaultValue,
|
|
||||||
Description = fieldDto.Description,
|
|
||||||
DisplayOrder = fieldDto.DisplayOrder
|
|
||||||
};
|
|
||||||
entity.Fields.Add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
entity = await _repository.InsertAsync(entity, autoSave: true);
|
|
||||||
|
|
||||||
return ObjectMapper.Map<SqlTable, SqlTableDto>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool FieldsHaveChanged(ICollection<SqlTableField> existingFields, List<CreateUpdateSqlTableFieldDto> inputFields)
|
|
||||||
{
|
|
||||||
if (existingFields.Count != inputFields.Count)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var existingFieldsList = existingFields.OrderBy(f => f.DisplayOrder).ToList();
|
|
||||||
var inputFieldsList = inputFields.OrderBy(f => f.DisplayOrder).ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < existingFieldsList.Count; i++)
|
|
||||||
{
|
|
||||||
var existing = existingFieldsList[i];
|
|
||||||
var input = inputFieldsList[i];
|
|
||||||
|
|
||||||
if (existing.Name != input.Name ||
|
|
||||||
existing.Type != input.Type ||
|
|
||||||
existing.IsRequired != input.IsRequired ||
|
|
||||||
existing.MaxLength != input.MaxLength ||
|
|
||||||
existing.IsUnique != input.IsUnique)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -29,8 +29,6 @@ public enum TableNameEnum
|
||||||
ForumCategory,
|
ForumCategory,
|
||||||
ForumTopic,
|
ForumTopic,
|
||||||
ForumPost,
|
ForumPost,
|
||||||
SqlTable,
|
|
||||||
SqlTableField,
|
|
||||||
CrudEndpoint,
|
CrudEndpoint,
|
||||||
CustomEndpoint,
|
CustomEndpoint,
|
||||||
CustomComponent,
|
CustomComponent,
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,8 @@ public static class PlatformConsts
|
||||||
public const string Get = CustomEndpoints + ".Get";
|
public const string Get = CustomEndpoints + ".Get";
|
||||||
public const string Post = CustomEndpoints + ".Post";
|
public const string Post = CustomEndpoints + ".Post";
|
||||||
|
|
||||||
|
public const string CrudEndpoints = Default + ".CrudEndpoints";
|
||||||
|
|
||||||
public static class DynamicServices
|
public static class DynamicServices
|
||||||
{
|
{
|
||||||
public const string DynamicService = Default + ".DynamicServices";
|
public const string DynamicService = Default + ".DynamicServices";
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,6 @@ public static class TableNameResolver
|
||||||
{ nameof(TableNameEnum.Note), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.Note), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
{ nameof(TableNameEnum.ReportCategory), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.ReportCategory), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
{ nameof(TableNameEnum.ReportTemplate), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.ReportTemplate), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
{ nameof(TableNameEnum.SqlTable), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
|
||||||
{ nameof(TableNameEnum.SqlTableField), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
|
||||||
{ nameof(TableNameEnum.CrudEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.CrudEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
{ nameof(TableNameEnum.CustomEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.CustomEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
{ nameof(TableNameEnum.CustomComponent), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
{ nameof(TableNameEnum.CustomComponent), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@ public class CrudEndpoint : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
public string CsharpCode { get; set; } = string.Empty;
|
public string CsharpCode { get; set; } = string.Empty;
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
// Foreign key to SqlTable
|
|
||||||
public Guid EntityId { get; set; }
|
|
||||||
public virtual SqlTable Entity { get; set; } = null!;
|
|
||||||
public CrudEndpoint()
|
public CrudEndpoint()
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Volo.Abp.Domain.Entities.Auditing;
|
|
||||||
using Volo.Abp.MultiTenancy;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Entities;
|
|
||||||
|
|
||||||
public class SqlTable : FullAuditedEntity<Guid>, IMultiTenant
|
|
||||||
{
|
|
||||||
public virtual Guid? TenantId { get; protected set; }
|
|
||||||
|
|
||||||
public string Menu { get; set; } = string.Empty;
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
|
||||||
public string TableName { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
public bool IsFullAuditedEntity { get; set; } = true;
|
|
||||||
public bool IsMultiTenant { get; set; } = false;
|
|
||||||
public string EndpointStatus { get; set; } = "pending"; // "pending" | "applied" | "failed"
|
|
||||||
|
|
||||||
public virtual ICollection<SqlTableField> Fields { get; set; } = [];
|
|
||||||
|
|
||||||
public SqlTable()
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(); // Burada erişim mümkün çünkü sınıfın içi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SqlTableField : FullAuditedEntity<Guid>
|
|
||||||
{
|
|
||||||
public Guid EntityId { get; set; } = Guid.NewGuid();
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Type { get; set; } = string.Empty;
|
|
||||||
public bool IsRequired { get; set; }
|
|
||||||
public int? MaxLength { get; set; }
|
|
||||||
public bool IsUnique { get; set; }
|
|
||||||
public string? DefaultValue { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public int DisplayOrder { get; set; } = 0;
|
|
||||||
|
|
||||||
public virtual SqlTable Entity { get; set; } = null!;
|
|
||||||
|
|
||||||
public SqlTableField()
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(); // Burada erişim mümkün çünkü sınıfın içi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using Sozsoft.Platform.Domain.DeveloperKit;
|
using Sozsoft.Platform.Domain.DeveloperKit;
|
||||||
using Sozsoft.Platform.Entities;
|
|
||||||
using Sozsoft.Platform.EntityFrameworkCore;
|
using Sozsoft.Platform.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
|
@ -8,65 +7,59 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp;
|
|
||||||
using Volo.Abp.Domain.Repositories;
|
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
public class DynamicEntityManager : IDynamicEntityManager
|
public class DynamicEntityManager : IDynamicEntityManager
|
||||||
{
|
{
|
||||||
private readonly IDbContextProvider<PlatformDbContext> _dbContextProvider;
|
private readonly IDbContextProvider<PlatformDbContext> _dbContextProvider;
|
||||||
private readonly IRepository<SqlTable, Guid> _sqlTableRepository;
|
|
||||||
|
|
||||||
public DynamicEntityManager(
|
public DynamicEntityManager(
|
||||||
IDbContextProvider<PlatformDbContext> dbContextProvider,
|
IDbContextProvider<PlatformDbContext> dbContextProvider)
|
||||||
IRepository<SqlTable, Guid> sqlTableRepository)
|
|
||||||
{
|
{
|
||||||
_dbContextProvider = dbContextProvider;
|
_dbContextProvider = dbContextProvider;
|
||||||
_sqlTableRepository = sqlTableRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<object>?> GetEntityListAsync(string entityName)
|
public async Task<List<object>?> GetEntityListAsync(string entityName)
|
||||||
{
|
{
|
||||||
return await ExecuteDynamicQuery(entityName, "GetList", null);
|
var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted");
|
||||||
|
var query = hasIsDeleted
|
||||||
|
? $"SELECT * FROM [{entityName}] WHERE IsDeleted = 0 OR IsDeleted IS NULL"
|
||||||
|
: $"SELECT * FROM [{entityName}]";
|
||||||
|
return await ExecuteRawQueryAsync(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object?> GetEntityByIdAsync(string entityName, Guid id)
|
public async Task<object?> GetEntityByIdAsync(string entityName, Guid id)
|
||||||
{
|
{
|
||||||
var result = await ExecuteDynamicQuery(entityName, "GetById", id);
|
var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted");
|
||||||
|
var query = hasIsDeleted
|
||||||
|
? $"SELECT * FROM [{entityName}] WHERE Id = '{id}' AND (IsDeleted = 0 OR IsDeleted IS NULL)"
|
||||||
|
: $"SELECT * FROM [{entityName}] WHERE Id = '{id}'";
|
||||||
|
var result = await ExecuteRawQueryAsync(query);
|
||||||
return result?.FirstOrDefault();
|
return result?.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object?> CreateEntityAsync(string entityName, JsonElement data)
|
public async Task<object?> CreateEntityAsync(string entityName, JsonElement data)
|
||||||
{
|
{
|
||||||
var entity = await GetEntityDefinitionAsync(entityName);
|
|
||||||
var tableName = entity.TableName;
|
|
||||||
var newId = Guid.NewGuid();
|
var newId = Guid.NewGuid();
|
||||||
|
var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted");
|
||||||
|
var hasCreationTime = await ColumnExistsAsync(entityName, "CreationTime");
|
||||||
|
|
||||||
var columns = new List<string> { "Id" };
|
var columns = new List<string> { "[Id]" };
|
||||||
var values = new List<string> { $"'{newId}'" };
|
var values = new List<string> { $"'{newId}'" };
|
||||||
|
|
||||||
foreach (var field in entity.Fields)
|
if (hasCreationTime) { columns.Add("[CreationTime]"); values.Add("SYSUTCDATETIME()"); }
|
||||||
|
if (hasIsDeleted) { columns.Add("[IsDeleted]"); values.Add("0"); }
|
||||||
|
|
||||||
|
foreach (var prop in data.EnumerateObject())
|
||||||
{
|
{
|
||||||
if (data.TryGetProperty(field.Name, out var fieldValue))
|
if (prop.NameEquals("id") || prop.NameEquals("Id"))
|
||||||
{
|
continue;
|
||||||
columns.Add($"[{field.Name}]");
|
|
||||||
values.Add(FormatValueForSql(fieldValue, field.Type));
|
columns.Add($"[{prop.Name}]");
|
||||||
}
|
values.Add(FormatValueForSql(prop.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.IsMultiTenant && data.TryGetProperty("TenantId", out var tenantIdValue))
|
var insertQuery = $"INSERT INTO [{entityName}] ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})";
|
||||||
{
|
|
||||||
columns.Add("[TenantId]");
|
|
||||||
values.Add($"'{tenantIdValue.GetGuid()}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.IsFullAuditedEntity)
|
|
||||||
{
|
|
||||||
columns.AddRange(new[] { "[CreationTime]", "[LastModificationTime]", "[IsDeleted]" });
|
|
||||||
values.AddRange(new[] { "SYSUTCDATETIME()", "NULL", "0" });
|
|
||||||
}
|
|
||||||
|
|
||||||
var insertQuery = $"INSERT INTO [{tableName}] ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})";
|
|
||||||
|
|
||||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
await dbContext.Database.ExecuteSqlRawAsync(insertQuery);
|
await dbContext.Database.ExecuteSqlRawAsync(insertQuery);
|
||||||
|
|
@ -76,26 +69,25 @@ public class DynamicEntityManager : IDynamicEntityManager
|
||||||
|
|
||||||
public async Task<object?> UpdateEntityAsync(string entityName, Guid id, JsonElement data)
|
public async Task<object?> UpdateEntityAsync(string entityName, Guid id, JsonElement data)
|
||||||
{
|
{
|
||||||
var entity = await GetEntityDefinitionAsync(entityName);
|
|
||||||
var tableName = entity.TableName;
|
|
||||||
|
|
||||||
var existing = await GetEntityByIdAsync(entityName, id);
|
var existing = await GetEntityByIdAsync(entityName, id);
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var setParts = new List<string>();
|
var setParts = new List<string>();
|
||||||
foreach (var field in entity.Fields)
|
|
||||||
{
|
|
||||||
if (data.TryGetProperty(field.Name, out var fieldValue))
|
|
||||||
{
|
|
||||||
setParts.Add($"[{field.Name}] = {FormatValueForSql(fieldValue, field.Type)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.IsFullAuditedEntity)
|
var hasLastModification = await ColumnExistsAsync(entityName, "LastModificationTime");
|
||||||
|
if (hasLastModification)
|
||||||
setParts.Add("[LastModificationTime] = SYSUTCDATETIME()");
|
setParts.Add("[LastModificationTime] = SYSUTCDATETIME()");
|
||||||
|
|
||||||
var updateQuery = $"UPDATE [{tableName}] SET {string.Join(", ", setParts)} WHERE Id = '{id}'";
|
foreach (var prop in data.EnumerateObject())
|
||||||
|
{
|
||||||
|
if (prop.NameEquals("id") || prop.NameEquals("Id"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
setParts.Add($"[{prop.Name}] = {FormatValueForSql(prop.Value)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateQuery = $"UPDATE [{entityName}] SET {string.Join(", ", setParts)} WHERE Id = '{id}'";
|
||||||
|
|
||||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
await dbContext.Database.ExecuteSqlRawAsync(updateQuery);
|
await dbContext.Database.ExecuteSqlRawAsync(updateQuery);
|
||||||
|
|
@ -105,37 +97,61 @@ public class DynamicEntityManager : IDynamicEntityManager
|
||||||
|
|
||||||
public async Task<bool> DeleteEntityAsync(string entityName, Guid id)
|
public async Task<bool> DeleteEntityAsync(string entityName, Guid id)
|
||||||
{
|
{
|
||||||
var entity = await GetEntityDefinitionAsync(entityName);
|
|
||||||
var tableName = entity.TableName;
|
|
||||||
|
|
||||||
var existing = await GetEntityByIdAsync(entityName, id);
|
var existing = await GetEntityByIdAsync(entityName, id);
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
string deleteQuery;
|
|
||||||
if (entity.IsFullAuditedEntity)
|
|
||||||
{
|
|
||||||
// Full audited entities always have soft delete
|
|
||||||
deleteQuery = $"UPDATE [{tableName}] SET [IsDeleted] = 1, [DeletionTime] = SYSUTCDATETIME() WHERE Id = '{id}'";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteQuery = $"DELETE FROM [{tableName}] WHERE Id = '{id}'";
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
var affected = await dbContext.Database.ExecuteSqlRawAsync(deleteQuery);
|
|
||||||
return affected > 0;
|
var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (hasIsDeleted)
|
||||||
|
{
|
||||||
|
var hasDeletionTime = await ColumnExistsAsync(entityName, "DeletionTime");
|
||||||
|
var softDeleteQuery = hasDeletionTime
|
||||||
|
? $"UPDATE [{entityName}] SET [IsDeleted] = 1, [DeletionTime] = SYSUTCDATETIME() WHERE Id = '{id}'"
|
||||||
|
: $"UPDATE [{entityName}] SET [IsDeleted] = 1 WHERE Id = '{id}'";
|
||||||
|
var affected = await dbContext.Database.ExecuteSqlRawAsync(softDeleteQuery);
|
||||||
|
return affected > 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var hardDeleteQuery = $"DELETE FROM [{entityName}] WHERE Id = '{id}'";
|
||||||
|
var affected = await dbContext.Database.ExecuteSqlRawAsync(hardDeleteQuery);
|
||||||
|
return affected > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
var hardDeleteQuery = $"DELETE FROM [{entityName}] WHERE Id = '{id}'";
|
||||||
|
var affected = await dbContext.Database.ExecuteSqlRawAsync(hardDeleteQuery);
|
||||||
|
return affected > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<object>> ExecuteDynamicQuery(string entityName, string operation, Guid? id)
|
private async Task<bool> ColumnExistsAsync(string tableName, string columnName)
|
||||||
{
|
{
|
||||||
var entity = await GetEntityDefinitionAsync(entityName);
|
var query = $"SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{tableName}' AND COLUMN_NAME = '{columnName}'";
|
||||||
var tableName = entity.TableName;
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
var query = operation == "GetList"
|
var connection = dbContext.Database.GetDbConnection();
|
||||||
? $"SELECT * FROM [{tableName}]"
|
await dbContext.Database.OpenConnectionAsync();
|
||||||
: $"SELECT * FROM [{tableName}] WHERE Id = '{id}'";
|
try
|
||||||
|
{
|
||||||
|
using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = query;
|
||||||
|
command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
|
||||||
|
var result = await command.ExecuteScalarAsync();
|
||||||
|
return Convert.ToInt32(result) > 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await dbContext.Database.CloseConnectionAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<object>> ExecuteRawQueryAsync(string query)
|
||||||
|
{
|
||||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
var connection = dbContext.Database.GetDbConnection();
|
var connection = dbContext.Database.GetDbConnection();
|
||||||
await dbContext.Database.OpenConnectionAsync();
|
await dbContext.Database.OpenConnectionAsync();
|
||||||
|
|
@ -144,8 +160,6 @@ public class DynamicEntityManager : IDynamicEntityManager
|
||||||
{
|
{
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = query;
|
command.CommandText = query;
|
||||||
|
|
||||||
// âï¸âï¸âï¸ Kritik satır:
|
|
||||||
command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
|
command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
|
||||||
|
|
||||||
using var reader = await command.ExecuteReaderAsync();
|
using var reader = await command.ExecuteReaderAsync();
|
||||||
|
|
@ -167,25 +181,19 @@ public class DynamicEntityManager : IDynamicEntityManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SqlTable> GetEntityDefinitionAsync(string entityName)
|
private static string FormatValueForSql(JsonElement value)
|
||||||
{
|
{
|
||||||
var queryable = await _sqlTableRepository.WithDetailsAsync(x => x.Fields);
|
return value.ValueKind switch
|
||||||
var entity = await queryable.FirstOrDefaultAsync(x => x.Name.ToLower() == entityName.ToLower());
|
|
||||||
|
|
||||||
return entity ?? throw new UserFriendlyException($"Entity '{entityName}' not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatValueForSql(JsonElement value, string type)
|
|
||||||
{
|
|
||||||
return type.ToLower() switch
|
|
||||||
{
|
{
|
||||||
"string" => $"'{value.GetString()?.Replace("'", "''")}'",
|
JsonValueKind.Number when value.TryGetInt64(out var l) => l.ToString(),
|
||||||
"number" => value.GetInt32().ToString(),
|
JsonValueKind.Number when value.TryGetDecimal(out var d) => d.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||||
"decimal" => value.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture),
|
JsonValueKind.True => "1",
|
||||||
"boolean" => value.GetBoolean() ? "1" : "0",
|
JsonValueKind.False => "0",
|
||||||
"date" => $"'{value.GetDateTime():yyyy-MM-dd HH:mm:ss}'",
|
JsonValueKind.Null => "NULL",
|
||||||
"guid" => $"'{value.GetGuid()}'",
|
JsonValueKind.String when value.TryGetGuid(out var g) => $"'{g}'",
|
||||||
_ => $"'{value.GetString()?.Replace("'", "''")}'",
|
JsonValueKind.String when value.TryGetDateTime(out var dt) => $"'{dt:yyyy-MM-dd HH:mm:ss}'",
|
||||||
|
JsonValueKind.String => $"'{value.GetString()?.Replace("'", "''")}'" ,
|
||||||
|
_ => "NULL",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,6 @@ public class PlatformDbContext :
|
||||||
public DbSet<ForumCategory> ForumCategories { get; set; }
|
public DbSet<ForumCategory> ForumCategories { get; set; }
|
||||||
public DbSet<ForumTopic> ForumTopics { get; set; }
|
public DbSet<ForumTopic> ForumTopics { get; set; }
|
||||||
public DbSet<ForumPost> ForumPosts { get; set; }
|
public DbSet<ForumPost> ForumPosts { get; set; }
|
||||||
public DbSet<SqlTable> CustomEntities { get; set; }
|
|
||||||
public DbSet<SqlTableField> EntityFields { get; set; }
|
|
||||||
public DbSet<CrudEndpoint> GeneratedEndpoints { get; set; }
|
public DbSet<CrudEndpoint> GeneratedEndpoints { get; set; }
|
||||||
public DbSet<CustomEndpoint> CustomEndpoints { get; set; }
|
public DbSet<CustomEndpoint> CustomEndpoints { get; set; }
|
||||||
public DbSet<CustomComponent> CustomComponents { get; set; }
|
public DbSet<CustomComponent> CustomComponents { get; set; }
|
||||||
|
|
@ -515,37 +513,6 @@ public class PlatformDbContext :
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<SqlTable>(b =>
|
|
||||||
{
|
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SqlTable)), Prefix.DbSchema);
|
|
||||||
b.ConfigureByConvention();
|
|
||||||
|
|
||||||
b.HasKey(x => x.Id);
|
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
|
||||||
b.Property(x => x.DisplayName).IsRequired().HasMaxLength(128);
|
|
||||||
b.Property(x => x.TableName).IsRequired().HasMaxLength(128);
|
|
||||||
b.Property(x => x.Description).HasMaxLength(512);
|
|
||||||
b.Property(x => x.EndpointStatus).IsRequired().HasMaxLength(64);
|
|
||||||
b.Property(x => x.Menu).IsRequired().HasMaxLength(128);
|
|
||||||
|
|
||||||
b.HasMany(x => x.Fields)
|
|
||||||
.WithOne(x => x.Entity)
|
|
||||||
.HasForeignKey(x => x.EntityId)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Entity<SqlTableField>(b =>
|
|
||||||
{
|
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SqlTableField)), Prefix.DbSchema);
|
|
||||||
b.ConfigureByConvention();
|
|
||||||
|
|
||||||
b.HasKey(x => x.Id);
|
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
|
||||||
b.Property(x => x.Type).IsRequired().HasMaxLength(64);
|
|
||||||
b.Property(x => x.Description).HasMaxLength(512);
|
|
||||||
b.Property(x => x.DefaultValue).HasMaxLength(256);
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Entity<CrudEndpoint>(b =>
|
builder.Entity<CrudEndpoint>(b =>
|
||||||
{
|
{
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.CrudEndpoint)), Prefix.DbSchema);
|
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.CrudEndpoint)), Prefix.DbSchema);
|
||||||
|
|
@ -557,11 +524,6 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.Path).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Path).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.OperationType).IsRequired().HasMaxLength(64);
|
b.Property(x => x.OperationType).IsRequired().HasMaxLength(64);
|
||||||
b.Property(x => x.CsharpCode).IsRequired();
|
b.Property(x => x.CsharpCode).IsRequired();
|
||||||
|
|
||||||
b.HasOne(x => x.Entity)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(x => x.EntityId)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<CustomEndpoint>(b =>
|
builder.Entity<CustomEndpoint>(b =>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260302174517_Initial")]
|
[Migration("20260302191746_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -1368,9 +1368,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<Guid>("EntityId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("EntityName")
|
b.Property<string>("EntityName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
|
|
@ -1414,8 +1411,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("EntityId");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3772,163 +3767,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.ToTable("Adm_T_SkillType", (string)null);
|
b.ToTable("Adm_T_SkillType", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("CreationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatorId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("CreatorId");
|
|
||||||
|
|
||||||
b.Property<Guid?>("DeleterId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("DeleterId");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DeletionTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("DeletionTime");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("EndpointStatus")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false)
|
|
||||||
.HasColumnName("IsDeleted");
|
|
||||||
|
|
||||||
b.Property<bool>("IsFullAuditedEntity")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsMultiTenant")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("LastModificationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("LastModifierId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("LastModifierId");
|
|
||||||
|
|
||||||
b.Property<string>("Menu")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("TableName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("TenantId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("TenantId");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_SqlTable", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("CreationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatorId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("CreatorId");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("DeleterId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("DeleterId");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DeletionTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("DeletionTime");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<int>("DisplayOrder")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<Guid>("EntityId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false)
|
|
||||||
.HasColumnName("IsDeleted");
|
|
||||||
|
|
||||||
b.Property<bool>("IsRequired")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsUnique")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("LastModificationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("LastModifierId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("LastModifierId");
|
|
||||||
|
|
||||||
b.Property<int?>("MaxLength")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Type")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("EntityId");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_SqlTableField", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
|
|
@ -6402,17 +6240,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.CrudEndpoint", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("EntityId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Entity");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.District", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.District", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
||||||
|
|
@ -6503,17 +6330,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Navigation("SkillType");
|
b.Navigation("SkillType");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity")
|
|
||||||
.WithMany("Fields")
|
|
||||||
.HasForeignKey("EntityId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Entity");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||||
|
|
@ -6753,11 +6569,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Navigation("Skills");
|
b.Navigation("Skills");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Fields");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Uoms");
|
b.Navigation("Uoms");
|
||||||
|
|
@ -591,6 +591,31 @@ namespace Sozsoft.Platform.Migrations
|
||||||
table.PrimaryKey("PK_Adm_T_Contact", x => x.Id);
|
table.PrimaryKey("PK_Adm_T_Contact", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Adm_T_CrudEndpoint",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
EntityName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
|
Method = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
||||||
|
Path = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
|
OperationType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
|
CsharpCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Adm_T_CrudEndpoint", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Adm_T_CustomComponent",
|
name: "Adm_T_CustomComponent",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
|
|
@ -986,34 +1011,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
table.PrimaryKey("PK_Adm_T_SkillType", x => x.Id);
|
table.PrimaryKey("PK_Adm_T_SkillType", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Adm_T_SqlTable",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
Menu = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
DisplayName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
TableName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
IsFullAuditedEntity = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
IsMultiTenant = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
EndpointStatus = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
|
||||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Adm_T_SqlTable", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Adm_T_UomCategory",
|
name: "Adm_T_UomCategory",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
|
|
@ -1945,71 +1942,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Adm_T_CrudEndpoint",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
EntityName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
Method = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
|
||||||
Path = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
|
||||||
OperationType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
CsharpCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
EntityId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
|
||||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Adm_T_CrudEndpoint", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Adm_T_CrudEndpoint_Adm_T_SqlTable_EntityId",
|
|
||||||
column: x => x.EntityId,
|
|
||||||
principalTable: "Adm_T_SqlTable",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Adm_T_SqlTableField",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
EntityId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
Type = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
IsRequired = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
MaxLength = table.Column<int>(type: "int", nullable: true),
|
|
||||||
IsUnique = table.Column<bool>(type: "bit", nullable: false),
|
|
||||||
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
|
||||||
DisplayOrder = table.Column<int>(type: "int", nullable: false),
|
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
|
||||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
|
||||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Adm_T_SqlTableField", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Adm_T_SqlTableField_Adm_T_SqlTable_EntityId",
|
|
||||||
column: x => x.EntityId,
|
|
||||||
principalTable: "Adm_T_SqlTable",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Adm_T_Uom",
|
name: "Adm_T_Uom",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
|
|
@ -2840,11 +2772,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
table: "Adm_T_BlogPost",
|
table: "Adm_T_BlogPost",
|
||||||
column: "Slug");
|
column: "Slug");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Adm_T_CrudEndpoint_EntityId",
|
|
||||||
table: "Adm_T_CrudEndpoint",
|
|
||||||
column: "EntityId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Adm_T_OrderItem_OrderId",
|
name: "IX_Adm_T_OrderItem_OrderId",
|
||||||
table: "Adm_T_OrderItem",
|
table: "Adm_T_OrderItem",
|
||||||
|
|
@ -2871,11 +2798,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
table: "Adm_T_SkillLevel",
|
table: "Adm_T_SkillLevel",
|
||||||
column: "SkillTypeId");
|
column: "SkillTypeId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Adm_T_SqlTableField_EntityId",
|
|
||||||
table: "Adm_T_SqlTableField",
|
|
||||||
column: "EntityId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Adm_T_Uom_UomCategoryId",
|
name: "IX_Adm_T_Uom_UomCategoryId",
|
||||||
table: "Adm_T_Uom",
|
table: "Adm_T_Uom",
|
||||||
|
|
@ -3151,9 +3073,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Adm_T_SkillLevel");
|
name: "Adm_T_SkillLevel");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Adm_T_SqlTableField");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Adm_T_Uom");
|
name: "Adm_T_Uom");
|
||||||
|
|
||||||
|
|
@ -3238,9 +3157,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Adm_T_SkillType");
|
name: "Adm_T_SkillType");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Adm_T_SqlTable");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Adm_T_UomCategory");
|
name: "Adm_T_UomCategory");
|
||||||
|
|
||||||
|
|
@ -1365,9 +1365,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<Guid>("EntityId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("EntityName")
|
b.Property<string>("EntityName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
|
|
@ -1411,8 +1408,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("EntityId");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3769,163 +3764,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.ToTable("Adm_T_SkillType", (string)null);
|
b.ToTable("Adm_T_SkillType", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("CreationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatorId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("CreatorId");
|
|
||||||
|
|
||||||
b.Property<Guid?>("DeleterId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("DeleterId");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DeletionTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("DeletionTime");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("EndpointStatus")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false)
|
|
||||||
.HasColumnName("IsDeleted");
|
|
||||||
|
|
||||||
b.Property<bool>("IsFullAuditedEntity")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsMultiTenant")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("LastModificationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("LastModifierId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("LastModifierId");
|
|
||||||
|
|
||||||
b.Property<string>("Menu")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("TableName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("TenantId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("TenantId");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_SqlTable", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("CreationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatorId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("CreatorId");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("DeleterId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("DeleterId");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DeletionTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("DeletionTime");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<int>("DisplayOrder")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<Guid>("EntityId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false)
|
|
||||||
.HasColumnName("IsDeleted");
|
|
||||||
|
|
||||||
b.Property<bool>("IsRequired")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsUnique")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("LastModificationTime");
|
|
||||||
|
|
||||||
b.Property<Guid?>("LastModifierId")
|
|
||||||
.HasColumnType("uniqueidentifier")
|
|
||||||
.HasColumnName("LastModifierId");
|
|
||||||
|
|
||||||
b.Property<int?>("MaxLength")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Type")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("EntityId");
|
|
||||||
|
|
||||||
b.ToTable("Adm_T_SqlTableField", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
|
|
@ -6399,17 +6237,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.CrudEndpoint", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("EntityId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Entity");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.District", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.District", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
||||||
|
|
@ -6500,17 +6327,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Navigation("SkillType");
|
b.Navigation("SkillType");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity")
|
|
||||||
.WithMany("Fields")
|
|
||||||
.HasForeignKey("EntityId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Entity");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||||
|
|
@ -6750,11 +6566,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Navigation("Skills");
|
b.Navigation("Skills");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Fields");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Uoms");
|
b.Navigation("Uoms");
|
||||||
|
|
|
||||||
|
|
@ -1,212 +0,0 @@
|
||||||
import React, { createContext, useContext, useState, useEffect } from "react";
|
|
||||||
import { developerKitService } from "@/services/developerKit.service";
|
|
||||||
import {
|
|
||||||
CrudEndpoint,
|
|
||||||
CreateUpdateSqlTableDto,
|
|
||||||
SqlTable,
|
|
||||||
} from "@/proxy/developerKit/models";
|
|
||||||
|
|
||||||
export const FIELD_TYPE_OPTIONS = [
|
|
||||||
{ label: "String", value: "string" },
|
|
||||||
{ label: "Number", value: "number" },
|
|
||||||
{ label: "Boolean", value: "boolean" },
|
|
||||||
{ label: "Date", value: "date" },
|
|
||||||
{ label: "Guid", value: "guid" },
|
|
||||||
{ label: "Decimal", value: "decimal" },
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type EntityFieldType = (typeof FIELD_TYPE_OPTIONS)[number]["value"];
|
|
||||||
|
|
||||||
interface EntityContextType {
|
|
||||||
entities: SqlTable[];
|
|
||||||
generatedEndpoints: CrudEndpoint[];
|
|
||||||
loading: boolean;
|
|
||||||
error: string | null;
|
|
||||||
// Entity operations
|
|
||||||
addEntity: (entity: CreateUpdateSqlTableDto) => Promise<void>;
|
|
||||||
updateEntity: (id: string, entity: CreateUpdateSqlTableDto) => Promise<void>;
|
|
||||||
deleteEntity: (id: string) => Promise<void>;
|
|
||||||
getEntity: (id: string) => SqlTable | undefined;
|
|
||||||
refreshEntities: () => Promise<void>;
|
|
||||||
toggleEntityActiveStatus: (id: string) => Promise<void>;
|
|
||||||
// Generated endpoint operations
|
|
||||||
generateCrudEndpoints: (entityId: string) => Promise<void>;
|
|
||||||
toggleEndpoint: (endpointId: string) => Promise<void>;
|
|
||||||
getEntityEndpoints: (entityId: string) => CrudEndpoint[];
|
|
||||||
deleteGeneratedEndpoint: (id: string) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EntityContext = createContext<EntityContextType | undefined>(undefined);
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-refresh/only-export-components
|
|
||||||
export const useEntities = () => {
|
|
||||||
const context = useContext(EntityContext);
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error("useEntities must be used within an EntityProvider");
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EntityProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
||||||
const [entities, setEntities] = useState<SqlTable[]>([]);
|
|
||||||
const [generatedEndpoints, setGeneratedEndpoints] = useState<CrudEndpoint[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const refreshEntities = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
const [entitiesData, generatedEndpointsData] = await Promise.all([
|
|
||||||
developerKitService.getSqlTables(),
|
|
||||||
developerKitService.getGeneratedEndpoints(),
|
|
||||||
]);
|
|
||||||
setEntities(entitiesData.items || []);
|
|
||||||
setGeneratedEndpoints(generatedEndpointsData.items || []);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to fetch data");
|
|
||||||
console.error("Failed to fetch data:", err);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
refreshEntities();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const addEntity = async (entityData: CreateUpdateSqlTableDto) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
const newEntity = await developerKitService.createSqlTable(entityData);
|
|
||||||
setEntities((prev) => [...prev, newEntity]);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to create entity");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateEntity = async (id: string, entityData: CreateUpdateSqlTableDto) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
const updatedEntity = await developerKitService.updateSqlTable(id, entityData);
|
|
||||||
setEntities((prev) => prev.map((e) => (e.id === id ? updatedEntity : e)));
|
|
||||||
await refreshEntities();
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to update entity");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteEntity = async (id: string) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
await developerKitService.deleteSqlTable(id);
|
|
||||||
setEntities((prev) => prev.filter((e) => e.id !== id));
|
|
||||||
setGeneratedEndpoints((prev) => prev.filter((ep) => ep.entityId !== id));
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to delete entity");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleEntityActiveStatus = async (id: string) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
await developerKitService.toggleSqlTableActiveStatus(id);
|
|
||||||
await refreshEntities();
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to toggle entity status");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getEntity = (id: string): SqlTable | undefined =>
|
|
||||||
entities.find((e) => e.id === id);
|
|
||||||
|
|
||||||
const generateCrudEndpoints = async (entityId: string) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
const endpointsData = await developerKitService.generateCrudEndpoints(entityId);
|
|
||||||
setGeneratedEndpoints((prev) => [
|
|
||||||
...prev.filter((e) => e.entityId !== entityId),
|
|
||||||
...(endpointsData.items || []),
|
|
||||||
]);
|
|
||||||
await refreshEntities();
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to generate CRUD endpoints");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleEndpoint = async (endpointId: string) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
const updatedEndpoint = await developerKitService.toggleGeneratedEndpoint(endpointId);
|
|
||||||
setGeneratedEndpoints((prev) =>
|
|
||||||
prev.map((e) => (e.id === endpointId ? updatedEndpoint : e)),
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to toggle endpoint");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getEntityEndpoints = (entityId: string): CrudEndpoint[] =>
|
|
||||||
generatedEndpoints.filter((e) => e.entityId === entityId);
|
|
||||||
|
|
||||||
const deleteGeneratedEndpoint = async (id: string) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
await developerKitService.deleteGeneratedEndpoint(id);
|
|
||||||
setGeneratedEndpoints((prev) => prev.filter((e) => e.id !== id));
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to delete generated endpoint");
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EntityContext.Provider
|
|
||||||
value={{
|
|
||||||
entities,
|
|
||||||
generatedEndpoints,
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
addEntity,
|
|
||||||
updateEntity,
|
|
||||||
deleteEntity,
|
|
||||||
getEntity,
|
|
||||||
refreshEntities,
|
|
||||||
toggleEntityActiveStatus,
|
|
||||||
generateCrudEndpoints,
|
|
||||||
toggleEndpoint,
|
|
||||||
getEntityEndpoints,
|
|
||||||
deleteGeneratedEndpoint,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</EntityContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,61 +1,7 @@
|
||||||
export interface SqlTable {
|
|
||||||
id: string;
|
|
||||||
menu: string;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
tableName: string;
|
|
||||||
description?: string;
|
|
||||||
isActive: boolean;
|
|
||||||
isFullAuditedEntity: boolean;
|
|
||||||
isMultiTenant: boolean;
|
|
||||||
endpointStatus: "pending" | "applied" | "failed";
|
|
||||||
fields: SqlTableField[];
|
|
||||||
creationTime: string;
|
|
||||||
lastModificationTime?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SqlTableField {
|
|
||||||
id: string;
|
|
||||||
entityId: string;
|
|
||||||
name: string;
|
|
||||||
type: "string" | "number" | "boolean" | "date" | "guid" | "decimal";
|
|
||||||
isRequired: boolean;
|
|
||||||
maxLength?: number;
|
|
||||||
isUnique?: boolean;
|
|
||||||
defaultValue?: string;
|
|
||||||
description?: string;
|
|
||||||
creationTime?: string;
|
|
||||||
lastModificationTime?: string;
|
|
||||||
displayOrder: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateUpdateSqlTableFieldDto {
|
|
||||||
id?: string;
|
|
||||||
name: string;
|
|
||||||
type: "string" | "number" | "boolean" | "date" | "guid" | "decimal";
|
|
||||||
isRequired: boolean;
|
|
||||||
maxLength?: number;
|
|
||||||
isUnique?: boolean;
|
|
||||||
defaultValue?: string;
|
|
||||||
description?: string;
|
|
||||||
displayOrder: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateUpdateSqlTableDto {
|
|
||||||
menu: string;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
tableName: string;
|
|
||||||
description?: string;
|
|
||||||
isActive: boolean;
|
|
||||||
isFullAuditedEntity: boolean;
|
|
||||||
isMultiTenant: boolean;
|
|
||||||
fields: CreateUpdateSqlTableFieldDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CrudEndpoint {
|
export interface CrudEndpoint {
|
||||||
id: string;
|
id: string;
|
||||||
entityId: string;
|
|
||||||
|
tenantId: string;
|
||||||
entityName: string;
|
entityName: string;
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||||
path: string;
|
path: string;
|
||||||
|
|
@ -67,7 +13,7 @@ export interface CrudEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateUpdateCrudEndpointDto {
|
export interface CreateUpdateCrudEndpointDto {
|
||||||
entityId: string;
|
tenantId: string;
|
||||||
entityName: string;
|
entityName: string;
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||||
path: string;
|
path: string;
|
||||||
|
|
@ -78,6 +24,7 @@ export interface CreateUpdateCrudEndpointDto {
|
||||||
|
|
||||||
export interface CustomComponent {
|
export interface CustomComponent {
|
||||||
id: string;
|
id: string;
|
||||||
|
tenantId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
code: string;
|
code: string;
|
||||||
props?: string;
|
props?: string;
|
||||||
|
|
@ -90,6 +37,7 @@ export interface CustomComponent {
|
||||||
|
|
||||||
export interface CustomComponentDto {
|
export interface CustomComponentDto {
|
||||||
id: string;
|
id: string;
|
||||||
|
tenantId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
code: string;
|
code: string;
|
||||||
props?: string;
|
props?: string;
|
||||||
|
|
@ -101,6 +49,7 @@ export interface CustomComponentDto {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateUpdateCustomComponentDto {
|
export interface CreateUpdateCustomComponentDto {
|
||||||
|
tenantId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
code: string;
|
code: string;
|
||||||
props?: string;
|
props?: string;
|
||||||
|
|
@ -108,3 +57,20 @@ export interface CreateUpdateCustomComponentDto {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
dependencies?: string;
|
dependencies?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RelationshipType = 'OneToOne' | 'OneToMany'
|
||||||
|
export type CascadeBehavior = 'NoAction' | 'Cascade' | 'SetNull' | 'Restrict'
|
||||||
|
|
||||||
|
export interface SqlTableRelation {
|
||||||
|
id: string
|
||||||
|
/** Existing DB constraint name — set when loaded from database (edit mode) */
|
||||||
|
constraintName?: string
|
||||||
|
relationshipType: RelationshipType
|
||||||
|
fkColumnName: string
|
||||||
|
referencedTable: string
|
||||||
|
referencedColumn: string
|
||||||
|
cascadeDelete: CascadeBehavior
|
||||||
|
cascadeUpdate: CascadeBehavior
|
||||||
|
isRequired: boolean
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
@ -4,69 +4,11 @@ import {
|
||||||
CrudEndpoint,
|
CrudEndpoint,
|
||||||
CreateUpdateCrudEndpointDto,
|
CreateUpdateCrudEndpointDto,
|
||||||
CreateUpdateCustomComponentDto,
|
CreateUpdateCustomComponentDto,
|
||||||
CreateUpdateSqlTableDto,
|
|
||||||
CustomComponent,
|
CustomComponent,
|
||||||
CustomComponentDto,
|
CustomComponentDto,
|
||||||
SqlTable,
|
|
||||||
} from '@/proxy/developerKit/models'
|
} from '@/proxy/developerKit/models'
|
||||||
|
|
||||||
class DeveloperKitService {
|
class DeveloperKitService {
|
||||||
async getSqlTables(): Promise<PagedResultDto<SqlTable>> {
|
|
||||||
const response = await apiService.fetchData<PagedResultDto<SqlTable>>({
|
|
||||||
url: '/api/app/sql-table',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async getActiveSqlTables(): Promise<PagedResultDto<SqlTable>> {
|
|
||||||
const response = await apiService.fetchData<PagedResultDto<SqlTable>>({
|
|
||||||
url: '/api/app/sql-table/active-tables',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSqlTable(id: string): Promise<SqlTable> {
|
|
||||||
const response = await apiService.fetchData<SqlTable>({
|
|
||||||
url: `/api/app/sql-table/${id}`,
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async createSqlTable(data: CreateUpdateSqlTableDto): Promise<SqlTable> {
|
|
||||||
const response = await apiService.fetchData<SqlTable>({
|
|
||||||
url: '/api/app/sql-table',
|
|
||||||
method: 'POST',
|
|
||||||
data: data as any,
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateSqlTable(id: string, entity: CreateUpdateSqlTableDto): Promise<SqlTable> {
|
|
||||||
const response = await apiService.fetchData<SqlTable>({
|
|
||||||
url: `/api/app/sql-table/${id}`,
|
|
||||||
method: 'PUT',
|
|
||||||
data: entity as any,
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteSqlTable(id: string): Promise<void> {
|
|
||||||
await apiService.fetchData<void>({
|
|
||||||
url: `/api/app/sql-table/${id}`,
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleSqlTableActiveStatus(id: string): Promise<void> {
|
|
||||||
await apiService.fetchData<void>({
|
|
||||||
url: `/api/app/sql-table/${id}/toggle-active-status`,
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom Component endpoints
|
// Custom Component endpoints
|
||||||
async getCustomComponents(): Promise<PagedResultDto<CustomComponentDto>> {
|
async getCustomComponents(): Promise<PagedResultDto<CustomComponentDto>> {
|
||||||
const response = await apiService.fetchData<PagedResultDto<CustomComponentDto>>({
|
const response = await apiService.fetchData<PagedResultDto<CustomComponentDto>>({
|
||||||
|
|
@ -121,14 +63,6 @@ class DeveloperKitService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generated Endpoint endpoints
|
// Generated Endpoint endpoints
|
||||||
async getGeneratedEndpoints(): Promise<PagedResultDto<CrudEndpoint>> {
|
|
||||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
|
||||||
url: '/api/app/crud-endpoint-generate',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async getActiveGeneratedEndpoints(): Promise<PagedResultDto<CrudEndpoint>> {
|
async getActiveGeneratedEndpoints(): Promise<PagedResultDto<CrudEndpoint>> {
|
||||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
||||||
url: '/api/app/crud-endpoint-generate/active-endpoints',
|
url: '/api/app/crud-endpoint-generate/active-endpoints',
|
||||||
|
|
@ -137,9 +71,25 @@ class DeveloperKitService {
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEndpointsByEntity(entityId: string): Promise<PagedResultDto<CrudEndpoint>> {
|
async toggleGeneratedEndpoint(id: string): Promise<CrudEndpoint> {
|
||||||
|
const response = await apiService.fetchData<CrudEndpoint>({
|
||||||
|
url: `/api/app/crud-endpoint-generate/${id}/toggle`,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateCrudEndpoints(entityName: string): Promise<PagedResultDto<CrudEndpoint>> {
|
||||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
||||||
url: `/api/app/crud-endpoint-generate/endpoints-by-entity/${entityId}`,
|
url: `/api/app/crud-endpoint-generate/generate-crud-endpoints/${entityName}`,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGeneratedListEndpoints(): Promise<PagedResultDto<CrudEndpoint>> {
|
||||||
|
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
||||||
|
url: '/api/app/crud-endpoint-generate',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
|
|
@ -180,38 +130,6 @@ class DeveloperKitService {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleGeneratedEndpoint(id: string): Promise<CrudEndpoint> {
|
|
||||||
const response = await apiService.fetchData<CrudEndpoint>({
|
|
||||||
url: `/api/app/crud-endpoint-generate/${id}/toggle`,
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateCrudEndpoints(entityId: string): Promise<PagedResultDto<CrudEndpoint>> {
|
|
||||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
|
||||||
url: `/api/app/crud-endpoint-generate/generate-crud-endpoints/${entityId}`,
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Health Check
|
|
||||||
async healthCheck(): Promise<{ status: string; timestamp: string }> {
|
|
||||||
try {
|
|
||||||
const response = await apiService.fetchData({
|
|
||||||
url: '/api/app/health',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
if (response.status === 200) {
|
|
||||||
return { status: 'healthy', timestamp: new Date().toISOString() }
|
|
||||||
}
|
|
||||||
return { status: 'unhealthy', timestamp: new Date().toISOString() }
|
|
||||||
} catch {
|
|
||||||
return { status: 'offline', timestamp: new Date().toISOString() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const developerKitService = new DeveloperKitService()
|
export const developerKitService = new DeveloperKitService()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import { developerKitService } from '@/services/developerKit.service'
|
import { developerKitService } from '@/services/developerKit.service'
|
||||||
import type { DataSourceDto } from '@/proxy/data-source'
|
import type { DataSourceDto } from '@/proxy/data-source'
|
||||||
import type { DatabaseTableDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseTableDto } from '@/proxy/sql-query-manager/models'
|
||||||
import type { CrudEndpoint, SqlTable } from '@/proxy/developerKit/models'
|
import type { CrudEndpoint } from '@/proxy/developerKit/models'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
|
|
||||||
|
|
@ -62,25 +62,14 @@ const METHOD_COLOR: Record<string, string> = {
|
||||||
const CrudEndpointManager: React.FC = () => {
|
const CrudEndpointManager: React.FC = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
// Local entity + endpoint state (no context dependency)
|
// Endpoint state
|
||||||
const [entities, setEntities] = useState<SqlTable[]>([])
|
|
||||||
const [generatedEndpoints, setGeneratedEndpoints] = useState<CrudEndpoint[]>([])
|
const [generatedEndpoints, setGeneratedEndpoints] = useState<CrudEndpoint[]>([])
|
||||||
|
|
||||||
const refreshData = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const [entitiesRes, endpointsRes] = await Promise.all([
|
|
||||||
developerKitService.getSqlTables(),
|
|
||||||
developerKitService.getGeneratedEndpoints(),
|
|
||||||
])
|
|
||||||
setEntities(entitiesRes.items || [])
|
|
||||||
setGeneratedEndpoints(endpointsRes.items || [])
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to refresh data', err)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshData()
|
developerKitService
|
||||||
|
.getGeneratedListEndpoints()
|
||||||
|
.then((res) => setGeneratedEndpoints(res.items || []))
|
||||||
|
.catch((err) => console.error('Failed to load endpoints', err))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Data source + tables
|
// Data source + tables
|
||||||
|
|
@ -135,19 +124,12 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
}, [selectedDataSource])
|
}, [selectedDataSource])
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
const getEntityForTable = useCallback(
|
|
||||||
(tableName: string): SqlTable | undefined =>
|
|
||||||
entities.find((e) => e.tableName.toLowerCase() === tableName.toLowerCase()),
|
|
||||||
[entities],
|
|
||||||
)
|
|
||||||
|
|
||||||
const getEndpointsForTable = useCallback(
|
const getEndpointsForTable = useCallback(
|
||||||
(tableName: string): CrudEndpoint[] => {
|
(tableName: string): CrudEndpoint[] => {
|
||||||
const entity = getEntityForTable(tableName)
|
const entityName = toPascalCase(tableName)
|
||||||
if (!entity) return []
|
return generatedEndpoints.filter((ep) => ep.entityName === entityName)
|
||||||
return generatedEndpoints.filter((ep) => ep.entityId === entity.id)
|
|
||||||
},
|
},
|
||||||
[entities, generatedEndpoints, getEntityForTable],
|
[generatedEndpoints],
|
||||||
)
|
)
|
||||||
|
|
||||||
const activeEndpointCount = (tableName: string) =>
|
const activeEndpointCount = (tableName: string) =>
|
||||||
|
|
@ -181,30 +163,10 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
const key = table.fullName
|
const key = table.fullName
|
||||||
setGeneratingFor(key)
|
setGeneratingFor(key)
|
||||||
try {
|
try {
|
||||||
let entity = getEntityForTable(table.tableName)
|
const entityName = toPascalCase(table.tableName)
|
||||||
if (!entity) {
|
const result = await developerKitService.generateCrudEndpoints(entityName)
|
||||||
const entityName = toPascalCase(table.tableName)
|
|
||||||
await developerKitService.createSqlTable({
|
|
||||||
menu: 'auto',
|
|
||||||
name: entityName,
|
|
||||||
displayName: entityName,
|
|
||||||
tableName: table.tableName,
|
|
||||||
description: `Auto-created from ${table.fullName}`,
|
|
||||||
isActive: true,
|
|
||||||
isFullAuditedEntity: false,
|
|
||||||
isMultiTenant: false,
|
|
||||||
fields: [],
|
|
||||||
})
|
|
||||||
const fresh = await developerKitService.getSqlTables()
|
|
||||||
setEntities(fresh.items || [])
|
|
||||||
entity = (fresh.items || []).find(
|
|
||||||
(e) => e.tableName.toLowerCase() === table.tableName.toLowerCase(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!entity) throw new Error('Entity could not be created')
|
|
||||||
const result = await developerKitService.generateCrudEndpoints(entity.id)
|
|
||||||
setGeneratedEndpoints((prev) => [
|
setGeneratedEndpoints((prev) => [
|
||||||
...prev.filter((ep) => ep.entityId !== entity!.id),
|
...prev.filter((ep) => ep.entityName !== entityName),
|
||||||
...(result.items || []),
|
...(result.items || []),
|
||||||
])
|
])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -352,7 +314,6 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
const tablesWithEndpoints = dbTables.filter((t) => allEndpointCount(t.tableName) > 0).length
|
const tablesWithEndpoints = dbTables.filter((t) => allEndpointCount(t.tableName) > 0).length
|
||||||
const totalActiveEndpoints = generatedEndpoints.filter((ep) => ep.isActive).length
|
const totalActiveEndpoints = generatedEndpoints.filter((ep) => ep.isActive).length
|
||||||
const selectedTableEndpoints = selectedTable ? getEndpointsForTable(selectedTable.tableName) : []
|
const selectedTableEndpoints = selectedTable ? getEndpointsForTable(selectedTable.tableName) : []
|
||||||
const selectedEntity = selectedTable ? getEntityForTable(selectedTable.tableName) : undefined
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full gap-4">
|
<div className="flex flex-col h-full gap-4">
|
||||||
|
|
@ -553,11 +514,6 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-slate-900">{selectedTable.schemaName}.{selectedTable.tableName}</h4>
|
<h4 className="text-slate-900">{selectedTable.schemaName}.{selectedTable.tableName}</h4>
|
||||||
</div>
|
</div>
|
||||||
{selectedEntity && (
|
|
||||||
<span className="ml-2 text-xs bg-blue-100 text-blue-700 px-2 py-1 rounded-full">
|
|
||||||
Entity: {selectedEntity.name}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{selectedTableEndpoints.length > 0 && (
|
{selectedTableEndpoints.length > 0 && (
|
||||||
|
|
@ -598,9 +554,7 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
<FaBolt className="text-3xl mb-3 text-slate-200" />
|
<FaBolt className="text-3xl mb-3 text-slate-200" />
|
||||||
<p className="font-medium">Henuz endpoint yok</p>
|
<p className="font-medium">Henuz endpoint yok</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
{selectedEntity
|
"CRUD Endpoint Olustur" butonuna tiklayin
|
||||||
? '"CRUD Endpoint Olustur" butonuna tiklayin'
|
|
||||||
: 'Bu tablo icin Sql Table otomatik olusturulacak ve 5 endpoint uretilecektir'}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -655,22 +609,6 @@ const CrudEndpointManager: React.FC = () => {
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
|
||||||
{ep.isActive ? (
|
|
||||||
<span className="text-xs text-green-600 font-medium">Aktif</span>
|
|
||||||
) : (
|
|
||||||
<span className="text-xs text-slate-400">Devre disi</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Copy path */}
|
|
||||||
<button
|
|
||||||
onClick={() => navigator.clipboard.writeText(ep.path)}
|
|
||||||
className="p-1.5 text-slate-400 hover:text-slate-700 transition-colors"
|
|
||||||
title="Path kopyala"
|
|
||||||
>
|
|
||||||
<FaCopy className="text-xs" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Expand */}
|
{/* Expand */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setExpandedEndpoint(isExpanded ? null : ep.id)}
|
onClick={() => setExpandedEndpoint(isExpanded ? null : ep.id)}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { useState, useCallback, useMemo, useEffect } from 'react'
|
import { useState, useCallback, useMemo, useEffect } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
|
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
|
|
@ -8,7 +9,6 @@ import {
|
||||||
FaTable,
|
FaTable,
|
||||||
FaCloudUploadAlt,
|
FaCloudUploadAlt,
|
||||||
FaCheck,
|
FaCheck,
|
||||||
FaChevronRight,
|
|
||||||
FaLink,
|
FaLink,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
FaTimes,
|
FaTimes,
|
||||||
|
|
@ -16,9 +16,8 @@ import {
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import { MenuService } from '@/services/menu.service'
|
import { MenuService } from '@/services/menu.service'
|
||||||
import { developerKitService } from '@/services/developerKit.service'
|
|
||||||
import type { CreateUpdateSqlTableDto, CreateUpdateSqlTableFieldDto } from '@/proxy/developerKit/models'
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -55,11 +54,6 @@ interface TableSettings {
|
||||||
menuPrefix: string
|
menuPrefix: string
|
||||||
entityName: string
|
entityName: string
|
||||||
tableName: string
|
tableName: string
|
||||||
displayName: string
|
|
||||||
description: string
|
|
||||||
isActive: boolean
|
|
||||||
isFullAudited: boolean
|
|
||||||
isMultiTenant: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TableDesignerDialogProps {
|
interface TableDesignerDialogProps {
|
||||||
|
|
@ -70,23 +64,6 @@ interface TableDesignerDialogProps {
|
||||||
initialTableData?: { schemaName: string; tableName: string } | null
|
initialTableData?: { schemaName: string; tableName: string } | null
|
||||||
}
|
}
|
||||||
|
|
||||||
type RelationshipType = 'OneToOne' | 'OneToMany'
|
|
||||||
type CascadeBehavior = 'NoAction' | 'Cascade' | 'SetNull' | 'Restrict'
|
|
||||||
|
|
||||||
interface FkRelationshipDefinition {
|
|
||||||
id: string
|
|
||||||
/** Existing DB constraint name — set when loaded from database (edit mode) */
|
|
||||||
constraintName?: string
|
|
||||||
relationshipType: RelationshipType
|
|
||||||
fkColumnName: string
|
|
||||||
referencedTable: string
|
|
||||||
referencedColumn: string
|
|
||||||
cascadeDelete: CascadeBehavior
|
|
||||||
cascadeUpdate: CascadeBehavior
|
|
||||||
isRequired: boolean
|
|
||||||
description: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Constants ────────────────────────────────────────────────────────────────
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
||||||
|
|
@ -104,6 +81,15 @@ const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const FULL_AUDIT_COLUMNS: ColumnDefinition[] = [
|
const FULL_AUDIT_COLUMNS: ColumnDefinition[] = [
|
||||||
|
{
|
||||||
|
id: '__Id',
|
||||||
|
columnName: 'Id',
|
||||||
|
dataType: 'uniqueidentifier',
|
||||||
|
maxLength: '',
|
||||||
|
isNullable: false,
|
||||||
|
defaultValue: 'NEWID()',
|
||||||
|
description: 'Primary key',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: '__CreationTime',
|
id: '__CreationTime',
|
||||||
columnName: 'CreationTime',
|
columnName: 'CreationTime',
|
||||||
|
|
@ -181,13 +167,13 @@ const CASCADE_OPTIONS: { value: CascadeBehavior; label: string }[] = [
|
||||||
{ value: 'Restrict', label: 'Restrict' },
|
{ value: 'Restrict', label: 'Restrict' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const EMPTY_FK: Omit<FkRelationshipDefinition, 'id'> = {
|
const EMPTY_FK: Omit<SqlTableRelation, 'id'> = {
|
||||||
relationshipType: 'OneToMany',
|
relationshipType: 'OneToMany',
|
||||||
fkColumnName: '',
|
fkColumnName: '',
|
||||||
referencedTable: '',
|
referencedTable: '',
|
||||||
referencedColumn: 'Id',
|
referencedColumn: 'Id',
|
||||||
cascadeDelete: 'NoAction',
|
cascadeDelete: 'NoAction',
|
||||||
cascadeUpdate: 'NoAction',
|
cascadeUpdate: 'Cascade',
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
|
|
@ -227,21 +213,17 @@ function colToSqlLine(col: ColumnDefinition, addComma = true): string {
|
||||||
function generateCreateTableSql(
|
function generateCreateTableSql(
|
||||||
columns: ColumnDefinition[],
|
columns: ColumnDefinition[],
|
||||||
settings: TableSettings,
|
settings: TableSettings,
|
||||||
relationships: FkRelationshipDefinition[],
|
relationships: SqlTableRelation[],
|
||||||
): string {
|
): string {
|
||||||
const tableName = settings.tableName || 'NewTable'
|
const tableName = settings.tableName || 'NewTable'
|
||||||
const fullTableName = `[dbo].[${tableName}]`
|
const fullTableName = `[dbo].[${tableName}]`
|
||||||
|
|
||||||
const userCols = columns.filter((c) => c.columnName.trim())
|
const userCols = columns.filter((c) => c.columnName.trim())
|
||||||
const auditCols = settings.isFullAudited ? FULL_AUDIT_COLUMNS : []
|
const allBodyCols = userCols
|
||||||
const tenantCols = settings.isMultiTenant ? [TENANT_COLUMN] : []
|
|
||||||
const allBodyCols = [...userCols, ...auditCols, ...tenantCols]
|
|
||||||
|
|
||||||
// When Full Audited: auto-add Id (PK) + audit cols
|
const hasIdCol = userCols.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
||||||
// When not Full Audited: user is responsible for their own Id column
|
const bodyLines = hasIdCol
|
||||||
const bodyLines = settings.isFullAudited
|
|
||||||
? [
|
? [
|
||||||
` [Id] uniqueidentifier NOT NULL DEFAULT NEWID(),`,
|
|
||||||
...allBodyCols.map((c) => colToSqlLine(c, true)),
|
...allBodyCols.map((c) => colToSqlLine(c, true)),
|
||||||
` CONSTRAINT [PK_${tableName}] PRIMARY KEY NONCLUSTERED ([Id])`,
|
` CONSTRAINT [PK_${tableName}] PRIMARY KEY NONCLUSTERED ([Id])`,
|
||||||
]
|
]
|
||||||
|
|
@ -266,8 +248,6 @@ function generateCreateTableSql(
|
||||||
const lines: string[] = [
|
const lines: string[] = [
|
||||||
`/* ── Table: ${fullTableName} ── */`,
|
`/* ── Table: ${fullTableName} ── */`,
|
||||||
...(settings.entityName ? [`/* Entity Name: ${settings.entityName} */`] : []),
|
...(settings.entityName ? [`/* Entity Name: ${settings.entityName} */`] : []),
|
||||||
...(settings.isFullAudited ? ['/* Full Audited Entity (Id + audit columns auto-added) */'] : []),
|
|
||||||
...(settings.isMultiTenant ? ['/* Multi-Tenant */'] : []),
|
|
||||||
...(fkLines.length > 0 ? ['/* Foreign Key Constraints */'] : []),
|
...(fkLines.length > 0 ? ['/* Foreign Key Constraints */'] : []),
|
||||||
'',
|
'',
|
||||||
`CREATE TABLE ${fullTableName}`,
|
`CREATE TABLE ${fullTableName}`,
|
||||||
|
|
@ -334,8 +314,8 @@ function generateAlterTableSql(
|
||||||
originalCols: ColumnDefinition[],
|
originalCols: ColumnDefinition[],
|
||||||
currentCols: ColumnDefinition[],
|
currentCols: ColumnDefinition[],
|
||||||
tableName: string,
|
tableName: string,
|
||||||
relationships: FkRelationshipDefinition[],
|
relationships: SqlTableRelation[],
|
||||||
originalRelationships: FkRelationshipDefinition[],
|
originalRelationships: SqlTableRelation[],
|
||||||
): string {
|
): string {
|
||||||
const fullTableName = `[dbo].[${tableName}]`
|
const fullTableName = `[dbo].[${tableName}]`
|
||||||
const lines: string[] = [`/* ── ALTER TABLE: ${fullTableName} ── */`, '']
|
const lines: string[] = [`/* ── ALTER TABLE: ${fullTableName} ── */`, '']
|
||||||
|
|
@ -412,7 +392,7 @@ function generateAlterTableSql(
|
||||||
const fkCascadeSql = (b: CascadeBehavior) =>
|
const fkCascadeSql = (b: CascadeBehavior) =>
|
||||||
b === 'NoAction' ? 'NO ACTION' : b.replace(/([A-Z])/g, ' $1').trim().toUpperCase()
|
b === 'NoAction' ? 'NO ACTION' : b.replace(/([A-Z])/g, ' $1').trim().toUpperCase()
|
||||||
|
|
||||||
const addFkSql = (rel: FkRelationshipDefinition) => {
|
const addFkSql = (rel: SqlTableRelation) => {
|
||||||
const cname = rel.constraintName ?? `FK_${tableName}_${rel.fkColumnName}`
|
const cname = rel.constraintName ?? `FK_${tableName}_${rel.fkColumnName}`
|
||||||
lines.push(`-- 🔗 FK Ekle: [${rel.fkColumnName}] → [${rel.referencedTable}]`)
|
lines.push(`-- 🔗 FK Ekle: [${rel.fkColumnName}] → [${rel.referencedTable}]`)
|
||||||
lines.push(`ALTER TABLE ${fullTableName}`)
|
lines.push(`ALTER TABLE ${fullTableName}`)
|
||||||
|
|
@ -424,9 +404,9 @@ function generateAlterTableSql(
|
||||||
lines.push('')
|
lines.push('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const origRelById = new Map<string, FkRelationshipDefinition>()
|
const origRelById = new Map<string, SqlTableRelation>()
|
||||||
originalRelationships.forEach((r) => origRelById.set(r.id, r))
|
originalRelationships.forEach((r) => origRelById.set(r.id, r))
|
||||||
const curRelById = new Map<string, FkRelationshipDefinition>()
|
const curRelById = new Map<string, SqlTableRelation>()
|
||||||
relationships.forEach((r) => curRelById.set(r.id, r))
|
relationships.forEach((r) => curRelById.set(r.id, r))
|
||||||
|
|
||||||
// Removed FKs → DROP CONSTRAINT
|
// Removed FKs → DROP CONSTRAINT
|
||||||
|
|
@ -478,18 +458,6 @@ function generateAlterTableSql(
|
||||||
return lines.join('\n')
|
return lines.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map TableDesigner data-type → SqlTableField type
|
|
||||||
function colTypeToEntityFieldType(
|
|
||||||
dt: SqlDataType,
|
|
||||||
): CreateUpdateSqlTableFieldDto['type'] {
|
|
||||||
if (dt === 'int' || dt === 'bigint') return 'number'
|
|
||||||
if (dt === 'bit') return 'boolean'
|
|
||||||
if (dt === 'datetime2' || dt === 'date') return 'date'
|
|
||||||
if (dt === 'uniqueidentifier') return 'guid'
|
|
||||||
if (dt === 'decimal' || dt === 'float' || dt === 'money') return 'decimal'
|
|
||||||
return 'string' // nvarchar, nvarchar(MAX), etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
const STEPS = ['Sütun Tasarımı', 'Entity Ayarları', 'İlişkiler', 'T-SQL Önizleme'] as const
|
const STEPS = ['Sütun Tasarımı', 'Entity Ayarları', 'İlişkiler', 'T-SQL Önizleme'] as const
|
||||||
type Step = 0 | 1 | 2 | 3
|
type Step = 0 | 1 | 2 | 3
|
||||||
|
|
||||||
|
|
@ -508,11 +476,6 @@ const DEFAULT_SETTINGS: TableSettings = {
|
||||||
menuPrefix: '',
|
menuPrefix: '',
|
||||||
entityName: '',
|
entityName: '',
|
||||||
tableName: '',
|
tableName: '',
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
isActive: true,
|
|
||||||
isFullAudited: true,
|
|
||||||
isMultiTenant: false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Component ────────────────────────────────────────────────────────────────
|
// ─── Component ────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -536,12 +499,12 @@ const SqlTableDesignerDialog = ({
|
||||||
const [settings, setSettings] = useState<TableSettings>(DEFAULT_SETTINGS)
|
const [settings, setSettings] = useState<TableSettings>(DEFAULT_SETTINGS)
|
||||||
const [menuOptions, setMenuOptions] = useState<MenuOption[]>([])
|
const [menuOptions, setMenuOptions] = useState<MenuOption[]>([])
|
||||||
const [menuLoading, setMenuLoading] = useState(false)
|
const [menuLoading, setMenuLoading] = useState(false)
|
||||||
const [relationships, setRelationships] = useState<FkRelationshipDefinition[]>([])
|
const [relationships, setRelationships] = useState<SqlTableRelation[]>([])
|
||||||
const [originalRelationships, setOriginalRelationships] = useState<FkRelationshipDefinition[]>([])
|
const [originalRelationships, setOriginalRelationships] = useState<SqlTableRelation[]>([])
|
||||||
const [fksLoading, setFksLoading] = useState(false)
|
const [fksLoading, setFksLoading] = useState(false)
|
||||||
const [fkModalOpen, setFkModalOpen] = useState(false)
|
const [fkModalOpen, setFkModalOpen] = useState(false)
|
||||||
const [editingFkId, setEditingFkId] = useState<string | null>(null)
|
const [editingFkId, setEditingFkId] = useState<string | null>(null)
|
||||||
const [fkForm, setFkForm] = useState<Omit<FkRelationshipDefinition, 'id'>>(EMPTY_FK)
|
const [fkForm, setFkForm] = useState<Omit<SqlTableRelation, 'id'>>(EMPTY_FK)
|
||||||
const [dbTables, setDbTables] = useState<{ schemaName: string; tableName: string }[]>([])
|
const [dbTables, setDbTables] = useState<{ schemaName: string; tableName: string }[]>([])
|
||||||
const [targetTableColumns, setTargetTableColumns] = useState<string[]>([])
|
const [targetTableColumns, setTargetTableColumns] = useState<string[]>([])
|
||||||
const [targetColsLoading, setTargetColsLoading] = useState(false)
|
const [targetColsLoading, setTargetColsLoading] = useState(false)
|
||||||
|
|
@ -583,42 +546,17 @@ const SqlTableDesignerDialog = ({
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => setColsLoading(false))
|
.finally(() => setColsLoading(false))
|
||||||
|
|
||||||
// Load SqlTable metadata (menu, entityName, displayName, flags…)
|
// Derive settings from table name (e.g. "Sas_D_EntityName" → menu: "Sas", entity: "EntityName")
|
||||||
developerKitService
|
const parts = initialTableData.tableName.split('_')
|
||||||
.getSqlTables()
|
const derivedMenu = parts[0] ?? ''
|
||||||
.then((res) => {
|
const derivedEntity = parts[parts.length - 1] ?? initialTableData.tableName
|
||||||
const match = (res.items ?? []).find(
|
setSettings((s) => ({
|
||||||
(e) => e.tableName.toLowerCase() === initialTableData.tableName.toLowerCase(),
|
...s,
|
||||||
)
|
menuValue: derivedMenu,
|
||||||
if (match) {
|
menuPrefix: derivedMenu,
|
||||||
setSettings((s) => ({
|
entityName: derivedEntity,
|
||||||
...s,
|
displayName: derivedEntity,
|
||||||
menuValue: match.menu,
|
}))
|
||||||
menuPrefix: match.menu,
|
|
||||||
entityName: match.name,
|
|
||||||
displayName: match.displayName,
|
|
||||||
description: match.description ?? '',
|
|
||||||
isActive: match.isActive,
|
|
||||||
isFullAudited: match.isFullAuditedEntity,
|
|
||||||
isMultiTenant: match.isMultiTenant,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
// Table not registered in developerKit — derive defaults from the table name.
|
|
||||||
// e.g. "Sas_T_SqlStoredProcedure" → menu: "Sas", entityName/displayName: "SqlStoredProcedure"
|
|
||||||
const parts = initialTableData.tableName.split('_')
|
|
||||||
const derivedMenu = parts[0] ?? ''
|
|
||||||
console.log('Derived menu from table name:', derivedMenu)
|
|
||||||
const derivedEntity = parts[parts.length - 1] ?? initialTableData.tableName
|
|
||||||
setSettings((s) => ({
|
|
||||||
...s,
|
|
||||||
menuValue: derivedMenu,
|
|
||||||
menuPrefix: derivedMenu,
|
|
||||||
entityName: derivedEntity,
|
|
||||||
displayName: derivedEntity,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
|
|
||||||
// Load existing FK constraints
|
// Load existing FK constraints
|
||||||
const fkQuery = [
|
const fkQuery = [
|
||||||
|
|
@ -649,7 +587,7 @@ const SqlTableDesignerDialog = ({
|
||||||
.executeQuery({ queryText: fkQuery, dataSourceCode: dataSource })
|
.executeQuery({ queryText: fkQuery, dataSourceCode: dataSource })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const rows: any[] = res.data?.data ?? []
|
const rows: any[] = res.data?.data ?? []
|
||||||
const fkDefs: FkRelationshipDefinition[] = rows.map((r) => ({
|
const fkDefs: SqlTableRelation[] = rows.map((r) => ({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
constraintName: r.constraintName,
|
constraintName: r.constraintName,
|
||||||
relationshipType: 'OneToMany' as RelationshipType,
|
relationshipType: 'OneToMany' as RelationshipType,
|
||||||
|
|
@ -687,6 +625,29 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
const addColumn = () => setColumns((prev) => [...prev, createEmptyColumn()])
|
const addColumn = () => setColumns((prev) => [...prev, createEmptyColumn()])
|
||||||
|
|
||||||
|
const clearAllColumns = () => setColumns([createEmptyColumn()])
|
||||||
|
|
||||||
|
const addFullAuditedColumns = () => {
|
||||||
|
const existingNames = new Set(columns.map((c) => c.columnName.trim().toLowerCase()))
|
||||||
|
const toAdd = FULL_AUDIT_COLUMNS.filter(
|
||||||
|
(c) => !existingNames.has(c.columnName.toLowerCase()),
|
||||||
|
)
|
||||||
|
setColumns((prev) => {
|
||||||
|
const nonEmpty = prev.filter((c) => c.columnName.trim() !== '')
|
||||||
|
return [...nonEmpty, ...toAdd.map((c) => ({ ...c })), createEmptyColumn()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addMultiTenantColumns = () => {
|
||||||
|
const existingNames = new Set(columns.map((c) => c.columnName.trim().toLowerCase()))
|
||||||
|
if (!existingNames.has(TENANT_COLUMN.columnName.toLowerCase())) {
|
||||||
|
setColumns((prev) => {
|
||||||
|
const nonEmpty = prev.filter((c) => c.columnName.trim() !== '')
|
||||||
|
return [...nonEmpty, { ...TENANT_COLUMN }, createEmptyColumn()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const removeColumn = (id: string) => setColumns((prev) => prev.filter((c) => c.id !== id))
|
const removeColumn = (id: string) => setColumns((prev) => prev.filter((c) => c.id !== id))
|
||||||
|
|
||||||
const moveColumn = (id: string, direction: 'up' | 'down') => {
|
const moveColumn = (id: string, direction: 'up' | 'down') => {
|
||||||
|
|
@ -759,7 +720,7 @@ const SqlTableDesignerDialog = ({
|
||||||
setFkModalOpen(true)
|
setFkModalOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openEditFk = (rel: FkRelationshipDefinition) => {
|
const openEditFk = (rel: SqlTableRelation) => {
|
||||||
setEditingFkId(rel.id)
|
setEditingFkId(rel.id)
|
||||||
const { id: _id, ...rest } = rel
|
const { id: _id, ...rest } = rel
|
||||||
setFkForm(rest)
|
setFkForm(rest)
|
||||||
|
|
@ -801,10 +762,7 @@ const SqlTableDesignerDialog = ({
|
||||||
const baseOk =
|
const baseOk =
|
||||||
!!settings.tableName.trim() && !!settings.entityName.trim() && !!settings.menuValue
|
!!settings.tableName.trim() && !!settings.entityName.trim() && !!settings.menuValue
|
||||||
if (!baseOk) return false
|
if (!baseOk) return false
|
||||||
if (!settings.isFullAudited) {
|
return columns.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
||||||
return columns.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -818,80 +776,6 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
// ── Deploy ─────────────────────────────────────────────────────────────────
|
// ── Deploy ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const syncSqlTableMetadata = async (deployedTableName: string) => {
|
|
||||||
try {
|
|
||||||
const namedCols = columns.filter((c) => c.columnName.trim())
|
|
||||||
|
|
||||||
// Find existing SqlTable record by table name
|
|
||||||
const listResult = await developerKitService.getSqlTables()
|
|
||||||
const existing = (listResult.items ?? []).find(
|
|
||||||
(e) => e.tableName.toLowerCase() === deployedTableName.toLowerCase(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
// Update: keep existing metadata, sync fields (match by name to preserve IDs)
|
|
||||||
const fieldDtos: CreateUpdateSqlTableFieldDto[] = namedCols.map((col, i) => {
|
|
||||||
const existingField = existing.fields?.find(
|
|
||||||
(f) => f.name.toLowerCase() === col.columnName.trim().toLowerCase(),
|
|
||||||
)
|
|
||||||
const maxLen = col.maxLength ? parseInt(col.maxLength, 10) || undefined : undefined
|
|
||||||
return {
|
|
||||||
id: existingField?.id,
|
|
||||||
name: col.columnName.trim(),
|
|
||||||
type: colTypeToEntityFieldType(col.dataType),
|
|
||||||
isRequired: !col.isNullable,
|
|
||||||
maxLength: maxLen,
|
|
||||||
isUnique: false,
|
|
||||||
defaultValue: col.defaultValue || undefined,
|
|
||||||
description: col.description || undefined,
|
|
||||||
displayOrder: i,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const updateDto: CreateUpdateSqlTableDto = {
|
|
||||||
menu: existing.menu,
|
|
||||||
name: existing.name,
|
|
||||||
displayName: existing.displayName,
|
|
||||||
tableName: existing.tableName,
|
|
||||||
description: existing.description,
|
|
||||||
isActive: existing.isActive,
|
|
||||||
isFullAuditedEntity: existing.isFullAuditedEntity,
|
|
||||||
isMultiTenant: existing.isMultiTenant,
|
|
||||||
fields: fieldDtos,
|
|
||||||
}
|
|
||||||
await developerKitService.updateSqlTable(existing.id, updateDto)
|
|
||||||
} else {
|
|
||||||
// Create new SqlTable record
|
|
||||||
const fieldDtos: CreateUpdateSqlTableFieldDto[] = namedCols.map((col, i) => {
|
|
||||||
const maxLen = col.maxLength ? parseInt(col.maxLength, 10) || undefined : undefined
|
|
||||||
return {
|
|
||||||
name: col.columnName.trim(),
|
|
||||||
type: colTypeToEntityFieldType(col.dataType),
|
|
||||||
isRequired: !col.isNullable,
|
|
||||||
maxLength: maxLen,
|
|
||||||
isUnique: false,
|
|
||||||
defaultValue: col.defaultValue || undefined,
|
|
||||||
description: col.description || undefined,
|
|
||||||
displayOrder: i,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const createDto: CreateUpdateSqlTableDto = {
|
|
||||||
menu: settings.menuValue,
|
|
||||||
name: settings.entityName || deployedTableName,
|
|
||||||
displayName: settings.displayName || deployedTableName,
|
|
||||||
tableName: deployedTableName,
|
|
||||||
description: settings.description || undefined,
|
|
||||||
isActive: settings.isActive,
|
|
||||||
isFullAuditedEntity: settings.isFullAudited,
|
|
||||||
isMultiTenant: settings.isMultiTenant,
|
|
||||||
fields: fieldDtos,
|
|
||||||
}
|
|
||||||
await developerKitService.createSqlTable(createDto)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Silent — SQL deploy already succeeded; metadata sync failure is non-critical
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDeploy = async () => {
|
const handleDeploy = async () => {
|
||||||
if (!dataSource) {
|
if (!dataSource) {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -910,8 +794,6 @@ const SqlTableDesignerDialog = ({
|
||||||
})
|
})
|
||||||
if (result.data.success) {
|
if (result.data.success) {
|
||||||
const deployedTable = settings.tableName || initialTableData?.tableName || ''
|
const deployedTable = settings.tableName || initialTableData?.tableName || ''
|
||||||
// Sync entity metadata to SqlTable / SqlTableField tables
|
|
||||||
await syncSqlTableMetadata(deployedTable)
|
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="success" title="Basarili">
|
<Notification type="success" title="Basarili">
|
||||||
Tablo basariyla {isEditMode ? 'güncellendi' : 'oluşturuldu'}: [dbo].[{deployedTable}]
|
Tablo basariyla {isEditMode ? 'güncellendi' : 'oluşturuldu'}: [dbo].[{deployedTable}]
|
||||||
|
|
@ -996,31 +878,27 @@ const SqlTableDesignerDialog = ({
|
||||||
)}
|
)}
|
||||||
{!colsLoading && (
|
{!colsLoading && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between py-3">
|
<div className="flex items-center justify-between py-2">
|
||||||
{isEditMode ? (
|
<div className="flex items-center gap-2">
|
||||||
<div className="p-2 bg-blue-50 dark:bg-blue-900/10 border border-blue-200 dark:border-blue-800 rounded text-xs text-blue-700 dark:text-blue-300">
|
<Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
|
||||||
📝 Mevcut sütunlar yüklendi. Değişiklikler T-SQL Önizleme adımında <strong>ALTER TABLE</strong> olarak gösterilecek.
|
Full Audited Entity Sütunları Ekle
|
||||||
|
|
</Button>
|
||||||
<span className="ml-1">
|
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
||||||
<span className="inline-block w-2.5 h-2.5 rounded bg-green-200 border border-green-400 mr-0.5"></span>Yeni
|
MultiTenant Sütunları Ekle
|
||||||
<span className="inline-block w-2.5 h-2.5 rounded bg-blue-200 border border-blue-400 mx-0.5 ml-2"></span>Ad Değişti (sp_rename)
|
</Button>
|
||||||
<span className="inline-block w-2.5 h-2.5 rounded bg-yellow-200 border border-yellow-400 mx-0.5 ml-2"></span>Tip/Null Değişti
|
</div>
|
||||||
</span>
|
<div className="flex items-center gap-2">
|
||||||
</div>
|
<Button size="xs" variant="solid" color="red-600" icon={<FaTrash />} onClick={clearAllColumns}>
|
||||||
) : (
|
Tüm Sütunları Sil
|
||||||
<div className="p-2 bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-800 rounded text-xs text-amber-700 dark:text-amber-300">
|
</Button>
|
||||||
Sonraki adımda seceğiniz{' '}
|
<Button size="xs" variant="solid" color="blue-600" icon={<FaPlus />} onClick={addColumn}>
|
||||||
<strong>Full Audited Entity</strong> / <strong>Multi-Tenant</strong> ayarlarina gore ek
|
Sütun Ekle
|
||||||
sutunlar da dahil edilecektir.
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<Button size="xs" variant="solid" color="blue-600" icon={<FaPlus />} onClick={addColumn}>
|
|
||||||
Ekle
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Header row */}
|
{/* Header row */}
|
||||||
<div className="grid grid-cols-12 gap-2 px-2 py-1 bg-gray-50 dark:bg-gray-800 rounded text-xs font-semibold text-gray-600 dark:text-gray-300">
|
<div className="grid grid-cols-12 gap-1 px-1 py-1 bg-gray-50 dark:bg-gray-800 rounded text-xs font-semibold text-gray-600 dark:text-gray-300">
|
||||||
<div className="col-span-3">Sutun Adi *</div>
|
<div className="col-span-3">Sutun Adi *</div>
|
||||||
<div className="col-span-3">Veri Tipi *</div>
|
<div className="col-span-3">Veri Tipi *</div>
|
||||||
<div className="col-span-1 text-center">Max</div>
|
<div className="col-span-1 text-center">Max</div>
|
||||||
|
|
@ -1032,7 +910,7 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
{/* Editable column rows */}
|
{/* Editable column rows */}
|
||||||
{duplicateColumnNames.size > 0 && (
|
{duplicateColumnNames.size > 0 && (
|
||||||
<div className="px-2 py-1.5 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded text-xs text-red-600 dark:text-red-400">
|
<div className="px-1 py-1.5 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded text-xs text-red-600 dark:text-red-400">
|
||||||
⚠️ Aynı isimde sütun tanımlanamaz:{' '}
|
⚠️ Aynı isimde sütun tanımlanamaz:{' '}
|
||||||
{[...duplicateColumnNames].map((n) => (
|
{[...duplicateColumnNames].map((n) => (
|
||||||
<code key={n} className="bg-red-100 dark:bg-red-900/40 px-1 rounded mr-1">
|
<code key={n} className="bg-red-100 dark:bg-red-900/40 px-1 rounded mr-1">
|
||||||
|
|
@ -1079,7 +957,7 @@ const SqlTableDesignerDialog = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={col.id}
|
key={col.id}
|
||||||
className={`grid grid-cols-12 gap-2 py-1 bg-white dark:bg-gray-800 rounded items-center ${rowBg}`}
|
className={`grid grid-cols-12 gap-2 bg-white dark:bg-gray-800 rounded items-center ${rowBg}`}
|
||||||
>
|
>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<input
|
<input
|
||||||
|
|
@ -1174,8 +1052,8 @@ const SqlTableDesignerDialog = ({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Id warning when Full Audited is off */}
|
{/* Id warning */}
|
||||||
{!isEditMode && !settings.isFullAudited &&
|
{!isEditMode &&
|
||||||
!columns.some((c) => c.columnName.trim().toLowerCase() === 'id') && (
|
!columns.some((c) => c.columnName.trim().toLowerCase() === 'id') && (
|
||||||
<div className="px-2 py-1.5 bg-orange-50 dark:bg-orange-900/20 border border-orange-300 dark:border-orange-700 rounded text-xs text-orange-700 dark:text-orange-300">
|
<div className="px-2 py-1.5 bg-orange-50 dark:bg-orange-900/20 border border-orange-300 dark:border-orange-700 rounded text-xs text-orange-700 dark:text-orange-300">
|
||||||
⚠️ <strong>Full Audited Entity</strong> seçilmedi. Tablonun birincil anahtarı için{' '}
|
⚠️ <strong>Full Audited Entity</strong> seçilmedi. Tablonun birincil anahtarı için{' '}
|
||||||
|
|
@ -1194,14 +1072,14 @@ const SqlTableDesignerDialog = ({
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
{/* Menu Name */}
|
{/* Menu Name */}
|
||||||
<div>
|
<div className="col-span-2">
|
||||||
<label className="block text-sm font-medium mb-1">
|
<label className="block text-sm font-medium mb-1">
|
||||||
Menu Name <span className="text-red-500">*</span>
|
Menu Name <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||||
value={settings.menuValue}
|
value={settings.menuValue}
|
||||||
disabled={menuLoading}
|
disabled={menuLoading || isEditMode} // Menu cannot be changed in edit mode (as it determines the table name)
|
||||||
onChange={(e) => onMenuChange(e.target.value)}
|
onChange={(e) => onMenuChange(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
|
|
@ -1221,6 +1099,7 @@ const SqlTableDesignerDialog = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
disabled={isEditMode} // Entity name (and thus table name) cannot be changed in edit mode
|
||||||
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||||
value={settings.entityName}
|
value={settings.entityName}
|
||||||
onChange={(e) => onEntityNameChange(e.target.value)}
|
onChange={(e) => onEntityNameChange(e.target.value)}
|
||||||
|
|
@ -1238,101 +1117,12 @@ const SqlTableDesignerDialog = ({
|
||||||
value={isEditMode ? (initialTableData?.tableName ?? '') : settings.tableName}
|
value={isEditMode ? (initialTableData?.tableName ?? '') : settings.tableName}
|
||||||
placeholder="Menu ve Entity Name seçince otomatik oluşur"
|
placeholder="Menu ve Entity Name seçince otomatik oluşur"
|
||||||
/>
|
/>
|
||||||
{isEditMode && (
|
|
||||||
<p className="text-xs text-blue-500 mt-0.5">Mevcut tablo — ad değiştirilemez.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Display Name */}
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium mb-1">Display Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
||||||
value={settings.displayName}
|
|
||||||
onChange={(e) => setSettings((s) => ({ ...s, displayName: e.target.value }))}
|
|
||||||
placeholder="e.g. Ürün, Kullanıcı, Sipariş"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Description */}
|
|
||||||
<div className="col-span-2">
|
|
||||||
<label className="block text-sm font-medium mb-1">Description</label>
|
|
||||||
<textarea
|
|
||||||
rows={2}
|
|
||||||
className="w-full px-3 py-2 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-none"
|
|
||||||
value={settings.description}
|
|
||||||
onChange={(e) => setSettings((s) => ({ ...s, description: e.target.value }))}
|
|
||||||
placeholder="Brief description of this entity"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Feature flags */}
|
|
||||||
<div className="border rounded-lg p-2 bg-gray-50 dark:bg-gray-800 space-y-2">
|
|
||||||
<h6 className="text-sm font-semibold mb-1">Entity Ozellikleri</h6>
|
|
||||||
|
|
||||||
{/* Active */}
|
{/* Warning: no Id column */}
|
||||||
<div className="flex items-start gap-3">
|
{!isEditMode &&
|
||||||
<Checkbox
|
|
||||||
checked={settings.isActive}
|
|
||||||
onChange={(checked) => setSettings((s) => ({ ...s, isActive: checked as boolean }))}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium">Active</p>
|
|
||||||
<p className="text-xs text-gray-500">Entity aktif mi olacak?</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Full Audited */}
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<Checkbox
|
|
||||||
checked={settings.isFullAudited}
|
|
||||||
onChange={(checked) =>
|
|
||||||
setSettings((s) => ({ ...s, isFullAudited: checked as boolean }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium">Full Audited Entity</p>
|
|
||||||
<p className="text-xs text-gray-500 mt-0.5">
|
|
||||||
{[
|
|
||||||
'Id',
|
|
||||||
'CreationTime',
|
|
||||||
'CreatorId',
|
|
||||||
'LastModificationTime',
|
|
||||||
'LastModifierId',
|
|
||||||
'IsDeleted',
|
|
||||||
'DeletionTime',
|
|
||||||
'DeleterId',
|
|
||||||
].map((c) => (
|
|
||||||
<code key={c} className="bg-gray-100 dark:bg-gray-700 px-1 rounded mr-0.5 text-xs">
|
|
||||||
{c}
|
|
||||||
</code>
|
|
||||||
))}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Multi-Tenant */}
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<Checkbox
|
|
||||||
checked={settings.isMultiTenant}
|
|
||||||
onChange={(checked) =>
|
|
||||||
setSettings((s) => ({ ...s, isMultiTenant: checked as boolean }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium">Multi-Tenant</p>
|
|
||||||
<p className="text-xs text-gray-500 mt-0.5">
|
|
||||||
<code className="bg-gray-100 dark:bg-gray-700 rounded text-xs">TenantId</code>{' '}
|
|
||||||
(uniqueidentifier NULL)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Warning: no Id column when Full Audited is off */}
|
|
||||||
{!isEditMode && !settings.isFullAudited &&
|
|
||||||
!columns.some((c) => c.columnName.trim().toLowerCase() === 'id') && (
|
!columns.some((c) => c.columnName.trim().toLowerCase() === 'id') && (
|
||||||
<div className="px-3 py-2 bg-red-50 dark:bg-red-900/20 border border-red-300 dark:border-red-700 rounded text-xs text-red-700 dark:text-red-300">
|
<div className="px-3 py-2 bg-red-50 dark:bg-red-900/20 border border-red-300 dark:border-red-700 rounded text-xs text-red-700 dark:text-red-300">
|
||||||
⛔ <strong>Full Audited Entity</strong> seçili değil. Geri dönüp{' '}
|
⛔ <strong>Full Audited Entity</strong> seçili değil. Geri dönüp{' '}
|
||||||
|
|
@ -1433,7 +1223,7 @@ const SqlTableDesignerDialog = ({
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
{/* FK Add/Edit Modal */}
|
{/* FK Add/Edit Modal */}
|
||||||
{fkModalOpen && (
|
{fkModalOpen && createPortal(
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-lg flex flex-col">
|
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-lg flex flex-col">
|
||||||
{/* Modal Header */}
|
{/* Modal Header */}
|
||||||
|
|
@ -1484,7 +1274,7 @@ const SqlTableDesignerDialog = ({
|
||||||
<select
|
<select
|
||||||
value={fkForm.fkColumnName}
|
value={fkForm.fkColumnName}
|
||||||
onChange={(e) => setFkForm((f) => ({ ...f, fkColumnName: e.target.value }))}
|
onChange={(e) => setFkForm((f) => ({ ...f, fkColumnName: e.target.value }))}
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<option value="">— Seçiniz —</option>
|
<option value="">— Seçiniz —</option>
|
||||||
{columns
|
{columns
|
||||||
|
|
@ -1507,12 +1297,12 @@ const SqlTableDesignerDialog = ({
|
||||||
setFkForm((f) => ({ ...f, referencedTable: val, referencedColumn: '' }))
|
setFkForm((f) => ({ ...f, referencedTable: val, referencedColumn: '' }))
|
||||||
loadTargetColumns(val)
|
loadTargetColumns(val)
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<option value="">— Seçiniz —</option>
|
<option value="">— Seçiniz —</option>
|
||||||
{dbTables.map((t) => (
|
{dbTables.map((t) => (
|
||||||
<option key={`${t.schemaName}.${t.tableName}`} value={t.tableName}>
|
<option key={`${t.schemaName}.${t.tableName}`} value={t.tableName}>
|
||||||
[{t.schemaName}].[{t.tableName}]
|
{t.tableName}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -1527,7 +1317,7 @@ const SqlTableDesignerDialog = ({
|
||||||
value={fkForm.referencedColumn}
|
value={fkForm.referencedColumn}
|
||||||
onChange={(e) => setFkForm((f) => ({ ...f, referencedColumn: e.target.value }))}
|
onChange={(e) => setFkForm((f) => ({ ...f, referencedColumn: e.target.value }))}
|
||||||
disabled={targetColsLoading || targetTableColumns.length === 0}
|
disabled={targetColsLoading || targetTableColumns.length === 0}
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500 disabled:opacity-60"
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500 disabled:opacity-60"
|
||||||
>
|
>
|
||||||
<option value="">— Önce hedef tablo seçin —</option>
|
<option value="">— Önce hedef tablo seçin —</option>
|
||||||
{targetTableColumns.map((col) => (
|
{targetTableColumns.map((col) => (
|
||||||
|
|
@ -1619,7 +1409,8 @@ const SqlTableDesignerDialog = ({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
|
document.body
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -1639,7 +1430,7 @@ const SqlTableDesignerDialog = ({
|
||||||
Kopyala
|
Kopyala
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="bg-gray-900 text-green-300 rounded-lg p-4 text-xs overflow-auto max-h-96 font-mono leading-relaxed whitespace-pre">
|
<pre className="bg-gray-900 text-green-300 rounded-lg p-4 text-xs overflow-auto max-h-96 leading-relaxed whitespace-pre">
|
||||||
{generatedSql}
|
{generatedSql}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue