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 CsharpCode { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public Guid EntityId { get; set; }
|
||||
|
||||
// Navigation property for display purposes
|
||||
public string? EntityDisplayName { get; set; }
|
||||
|
|
@ -25,6 +24,5 @@ public class CreateUpdateCrudEndpointDto
|
|||
public string OperationType { get; set; } = string.Empty;
|
||||
public string CsharpCode { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public Guid EntityId { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ public interface ICrudEndpointAppService : ICrudAppService<
|
|||
CreateUpdateCrudEndpointDto>
|
||||
{
|
||||
Task<List<CrudEndpointDto>> GetActiveEndpointsAsync();
|
||||
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(Guid entityId);
|
||||
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(Guid entityId);
|
||||
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
||||
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 Sozsoft.Platform.DeveloperKit;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sozsoft.Platform;
|
||||
|
||||
namespace Platform.Api.Application;
|
||||
|
||||
[Authorize]
|
||||
public class CrudEndpointGenerateAppService : CrudAppService<
|
||||
CrudEndpoint,
|
||||
CrudEndpointDto,
|
||||
|
|
@ -19,116 +19,95 @@ public class CrudEndpointGenerateAppService : CrudAppService<
|
|||
PagedAndSortedResultRequestDto,
|
||||
CreateUpdateCrudEndpointDto>, ICrudEndpointAppService
|
||||
{
|
||||
private readonly IRepository<SqlTable, Guid> _entityRepository;
|
||||
private readonly IRepository<CrudEndpoint, Guid> _endpointRepository;
|
||||
|
||||
public CrudEndpointGenerateAppService(
|
||||
IRepository<CrudEndpoint, Guid> repository,
|
||||
IRepository<SqlTable, Guid> entityRepository,
|
||||
IRepository<CrudEndpoint, Guid> endpointRepository)
|
||||
IRepository<CrudEndpoint, Guid> 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()
|
||||
{
|
||||
var endpoints = await Repository.GetListAsync(x => x.IsActive);
|
||||
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);
|
||||
}
|
||||
|
||||
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 entityQueryable = await _entityRepository.GetQueryableAsync();
|
||||
var entity = await entityQueryable
|
||||
.Include(x => x.Fields)
|
||||
.FirstOrDefaultAsync(x => x.Id == entityId);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
throw new Exception($"Entity with ID {entityId} not found");
|
||||
}
|
||||
var endpoint = await Repository.GetAsync(id);
|
||||
endpoint.IsActive = !endpoint.IsActive;
|
||||
await Repository.UpdateAsync(endpoint, autoSave: true);
|
||||
return ObjectMapper.Map<CrudEndpoint, CrudEndpointDto>(endpoint);
|
||||
}
|
||||
|
||||
[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
|
||||
var endpoints = new List<CrudEndpoint>();
|
||||
var entityName = entity.Name;
|
||||
var entityDisplayName = entity.DisplayName;
|
||||
|
||||
endpoints.Add(new CrudEndpoint
|
||||
var endpoints = new List<CrudEndpoint>
|
||||
{
|
||||
EntityId = entityId,
|
||||
EntityName = entityName,
|
||||
Method = "GET",
|
||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}",
|
||||
OperationType = "GetList",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateGetAllCode(entityName, entityDisplayName)
|
||||
});
|
||||
|
||||
endpoints.Add(new CrudEndpoint
|
||||
{
|
||||
EntityId = entityId,
|
||||
EntityName = entityName,
|
||||
Method = "GET",
|
||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}",
|
||||
OperationType = "GetById",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateGetByIdCode(entityName, entityDisplayName)
|
||||
});
|
||||
|
||||
endpoints.Add(new CrudEndpoint
|
||||
{
|
||||
EntityId = entityId,
|
||||
EntityName = entityName,
|
||||
Method = "POST",
|
||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}",
|
||||
OperationType = "Create",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateCreateCode(entityName, entityDisplayName)
|
||||
});
|
||||
|
||||
endpoints.Add(new CrudEndpoint
|
||||
{
|
||||
EntityId = entityId,
|
||||
EntityName = entityName,
|
||||
Method = "PUT",
|
||||
Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}",
|
||||
OperationType = "Update",
|
||||
IsActive = true,
|
||||
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)
|
||||
});
|
||||
new() {
|
||||
EntityName = entityName,
|
||||
Method = "GET",
|
||||
Path = $"/api/app/crudendpoint/{entityName}",
|
||||
OperationType = "GetList",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateGetAllCode(entityName)
|
||||
},
|
||||
new() {
|
||||
EntityName = entityName,
|
||||
Method = "GET",
|
||||
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||
OperationType = "GetById",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateGetByIdCode(entityName)
|
||||
},
|
||||
new() {
|
||||
EntityName = entityName,
|
||||
Method = "POST",
|
||||
Path = $"/api/app/crudendpoint/{entityName}",
|
||||
OperationType = "Create",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateCreateCode(entityName)
|
||||
},
|
||||
new() {
|
||||
EntityName = entityName,
|
||||
Method = "PUT",
|
||||
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||
OperationType = "Update",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateUpdateCode(entityName)
|
||||
},
|
||||
new() {
|
||||
EntityName = entityName,
|
||||
Method = "DELETE",
|
||||
Path = $"/api/app/crudendpoint/{entityName}/{{id}}",
|
||||
OperationType = "Delete",
|
||||
IsActive = true,
|
||||
CsharpCode = GenerateDeleteCode(entityName)
|
||||
}
|
||||
};
|
||||
|
||||
// Var olanları sil
|
||||
var existingEndpoints = await _endpointRepository
|
||||
.GetListAsync(x => x.EntityId == entityId);
|
||||
var existingEndpoints = await Repository
|
||||
.GetListAsync(x => x.EntityName == entityName);
|
||||
|
||||
await _endpointRepository.DeleteManyAsync(existingEndpoints);
|
||||
await Repository.DeleteManyAsync(existingEndpoints);
|
||||
|
||||
// Yeni endpointleri ekle
|
||||
await _endpointRepository.InsertManyAsync(endpoints, autoSave: true);
|
||||
|
||||
// Entity endpoint durumu güncelle
|
||||
entity.EndpointStatus = "Uygulandı";
|
||||
await _entityRepository.UpdateAsync(entity, autoSave: true);
|
||||
await Repository.InsertManyAsync(endpoints, autoSave: true);
|
||||
|
||||
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]
|
||||
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}}"")]
|
||||
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);
|
||||
if (entity == null)
|
||||
{{
|
||||
return NotFound($""{displayName} with ID {{id}} not found"");
|
||||
return NotFound($""{entityName} with ID {{id}} not found"");
|
||||
}}
|
||||
return Ok(entity);
|
||||
}}";
|
||||
}
|
||||
|
||||
private string GenerateCreateCode(string entityName, string displayName)
|
||||
private string GenerateCreateCode(string entityName)
|
||||
{
|
||||
return $@"[HttpPost]
|
||||
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}}"")]
|
||||
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))
|
||||
{{
|
||||
return NotFound($""{displayName} with ID {{id}} not found"");
|
||||
return NotFound($""{entityName} with ID {{id}} not found"");
|
||||
}}
|
||||
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}}"")]
|
||||
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);
|
||||
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()});
|
||||
|
|
@ -226,5 +205,3 @@ private async Task<bool> {entityName}ExistsAsync(Guid id)
|
|||
}}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,21 +7,12 @@ public class DeveloperKitAutoMapperProfile : Profile
|
|||
{
|
||||
public DeveloperKitAutoMapperProfile()
|
||||
{
|
||||
// SqlTable mappings
|
||||
CreateMap<SqlTable, SqlTableDto>();
|
||||
CreateMap<CreateUpdateSqlTableDto, SqlTable>();
|
||||
|
||||
// EntityField mappings
|
||||
CreateMap<SqlTableField, SqlTableFieldDto>();
|
||||
CreateMap<CreateUpdateSqlTableFieldDto, SqlTableField>();
|
||||
|
||||
// CustomComponent mappings
|
||||
CreateMap<CustomComponent, CustomComponentDto>();
|
||||
CreateMap<CreateUpdateCustomComponentDto, CustomComponent>();
|
||||
|
||||
// GeneratedEndpoint mappings
|
||||
CreateMap<CrudEndpoint, CrudEndpointDto>()
|
||||
.ForMember(dest => dest.EntityDisplayName, opt => opt.MapFrom(src => src.Entity != null ? src.Entity.DisplayName : null));
|
||||
CreateMap<CrudEndpoint, CrudEndpointDto>();
|
||||
CreateMap<CreateUpdateCrudEndpointDto, CrudEndpoint>();
|
||||
|
||||
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,
|
||||
ForumTopic,
|
||||
ForumPost,
|
||||
SqlTable,
|
||||
SqlTableField,
|
||||
CrudEndpoint,
|
||||
CustomEndpoint,
|
||||
CustomComponent,
|
||||
|
|
|
|||
|
|
@ -332,6 +332,8 @@ public static class PlatformConsts
|
|||
public const string Get = CustomEndpoints + ".Get";
|
||||
public const string Post = CustomEndpoints + ".Post";
|
||||
|
||||
public const string CrudEndpoints = Default + ".CrudEndpoints";
|
||||
|
||||
public static class DynamicServices
|
||||
{
|
||||
public const string DynamicService = Default + ".DynamicServices";
|
||||
|
|
|
|||
|
|
@ -68,8 +68,6 @@ public static class TableNameResolver
|
|||
{ nameof(TableNameEnum.Note), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.ReportCategory), (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.CustomEndpoint), (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 bool IsActive { get; set; } = true;
|
||||
|
||||
// Foreign key to SqlTable
|
||||
public Guid EntityId { get; set; }
|
||||
public virtual SqlTable Entity { get; set; } = null!;
|
||||
public CrudEndpoint()
|
||||
{
|
||||
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.Entities;
|
||||
using Sozsoft.Platform.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
|
@ -8,65 +7,59 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.EntityFrameworkCore;
|
||||
|
||||
public class DynamicEntityManager : IDynamicEntityManager
|
||||
{
|
||||
private readonly IDbContextProvider<PlatformDbContext> _dbContextProvider;
|
||||
private readonly IRepository<SqlTable, Guid> _sqlTableRepository;
|
||||
|
||||
public DynamicEntityManager(
|
||||
IDbContextProvider<PlatformDbContext> dbContextProvider,
|
||||
IRepository<SqlTable, Guid> sqlTableRepository)
|
||||
IDbContextProvider<PlatformDbContext> dbContextProvider)
|
||||
{
|
||||
_dbContextProvider = dbContextProvider;
|
||||
_sqlTableRepository = sqlTableRepository;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
public async Task<object?> CreateEntityAsync(string entityName, JsonElement data)
|
||||
{
|
||||
var entity = await GetEntityDefinitionAsync(entityName);
|
||||
var tableName = entity.TableName;
|
||||
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}'" };
|
||||
|
||||
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))
|
||||
{
|
||||
columns.Add($"[{field.Name}]");
|
||||
values.Add(FormatValueForSql(fieldValue, field.Type));
|
||||
}
|
||||
if (prop.NameEquals("id") || prop.NameEquals("Id"))
|
||||
continue;
|
||||
|
||||
columns.Add($"[{prop.Name}]");
|
||||
values.Add(FormatValueForSql(prop.Value));
|
||||
}
|
||||
|
||||
if (entity.IsMultiTenant && data.TryGetProperty("TenantId", out var tenantIdValue))
|
||||
{
|
||||
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 insertQuery = $"INSERT INTO [{entityName}] ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})";
|
||||
|
||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||
await dbContext.Database.ExecuteSqlRawAsync(insertQuery);
|
||||
|
|
@ -76,26 +69,25 @@ public class DynamicEntityManager : IDynamicEntityManager
|
|||
|
||||
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);
|
||||
if (existing == null)
|
||||
return null;
|
||||
|
||||
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()");
|
||||
|
||||
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();
|
||||
await dbContext.Database.ExecuteSqlRawAsync(updateQuery);
|
||||
|
|
@ -105,37 +97,61 @@ public class DynamicEntityManager : IDynamicEntityManager
|
|||
|
||||
public async Task<bool> DeleteEntityAsync(string entityName, Guid id)
|
||||
{
|
||||
var entity = await GetEntityDefinitionAsync(entityName);
|
||||
var tableName = entity.TableName;
|
||||
|
||||
var existing = await GetEntityByIdAsync(entityName, id);
|
||||
if (existing == null)
|
||||
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 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 tableName = entity.TableName;
|
||||
var query = operation == "GetList"
|
||||
? $"SELECT * FROM [{tableName}]"
|
||||
: $"SELECT * FROM [{tableName}] WHERE Id = '{id}'";
|
||||
var query = $"SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{tableName}' AND COLUMN_NAME = '{columnName}'";
|
||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||
var connection = dbContext.Database.GetDbConnection();
|
||||
await dbContext.Database.OpenConnectionAsync();
|
||||
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 connection = dbContext.Database.GetDbConnection();
|
||||
await dbContext.Database.OpenConnectionAsync();
|
||||
|
|
@ -144,8 +160,6 @@ public class DynamicEntityManager : IDynamicEntityManager
|
|||
{
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = query;
|
||||
|
||||
// âï¸âï¸âï¸ Kritik satır:
|
||||
command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
|
||||
|
||||
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);
|
||||
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
|
||||
return value.ValueKind switch
|
||||
{
|
||||
"string" => $"'{value.GetString()?.Replace("'", "''")}'",
|
||||
"number" => value.GetInt32().ToString(),
|
||||
"decimal" => value.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
"boolean" => value.GetBoolean() ? "1" : "0",
|
||||
"date" => $"'{value.GetDateTime():yyyy-MM-dd HH:mm:ss}'",
|
||||
"guid" => $"'{value.GetGuid()}'",
|
||||
_ => $"'{value.GetString()?.Replace("'", "''")}'",
|
||||
JsonValueKind.Number when value.TryGetInt64(out var l) => l.ToString(),
|
||||
JsonValueKind.Number when value.TryGetDecimal(out var d) => d.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
JsonValueKind.True => "1",
|
||||
JsonValueKind.False => "0",
|
||||
JsonValueKind.Null => "NULL",
|
||||
JsonValueKind.String when value.TryGetGuid(out var g) => $"'{g}'",
|
||||
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<ForumTopic> ForumTopics { 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<CustomEndpoint> CustomEndpoints { get; set; }
|
||||
public DbSet<CustomComponent> CustomComponents { get; set; }
|
||||
|
|
@ -515,37 +513,6 @@ public class PlatformDbContext :
|
|||
.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 =>
|
||||
{
|
||||
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.OperationType).IsRequired().HasMaxLength(64);
|
||||
b.Property(x => x.CsharpCode).IsRequired();
|
||||
|
||||
b.HasOne(x => x.Entity)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.EntityId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<CustomEndpoint>(b =>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260302174517_Initial")]
|
||||
[Migration("20260302191746_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -1368,9 +1368,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<Guid>("EntityId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("EntityName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
|
|
@ -1414,8 +1411,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EntityId");
|
||||
|
||||
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
||||
});
|
||||
|
||||
|
|
@ -3772,163 +3767,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
|
|
@ -6402,17 +6240,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
.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 =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
||||
|
|
@ -6503,17 +6330,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||
|
|
@ -6753,11 +6569,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("Skills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
||||
{
|
||||
b.Navigation("Fields");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||
{
|
||||
b.Navigation("Uoms");
|
||||
|
|
@ -591,6 +591,31 @@ namespace Sozsoft.Platform.Migrations
|
|||
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(
|
||||
name: "Adm_T_CustomComponent",
|
||||
columns: table => new
|
||||
|
|
@ -986,34 +1011,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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(
|
||||
name: "Adm_T_UomCategory",
|
||||
columns: table => new
|
||||
|
|
@ -1945,71 +1942,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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(
|
||||
name: "Adm_T_Uom",
|
||||
columns: table => new
|
||||
|
|
@ -2840,11 +2772,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
table: "Adm_T_BlogPost",
|
||||
column: "Slug");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_CrudEndpoint_EntityId",
|
||||
table: "Adm_T_CrudEndpoint",
|
||||
column: "EntityId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_OrderItem_OrderId",
|
||||
table: "Adm_T_OrderItem",
|
||||
|
|
@ -2871,11 +2798,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
table: "Adm_T_SkillLevel",
|
||||
column: "SkillTypeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SqlTableField_EntityId",
|
||||
table: "Adm_T_SqlTableField",
|
||||
column: "EntityId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_Uom_UomCategoryId",
|
||||
table: "Adm_T_Uom",
|
||||
|
|
@ -3151,9 +3073,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SkillLevel");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SqlTableField");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_Uom");
|
||||
|
||||
|
|
@ -3238,9 +3157,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SkillType");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SqlTable");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_UomCategory");
|
||||
|
||||
|
|
@ -1365,9 +1365,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<Guid>("EntityId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("EntityName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
|
|
@ -1411,8 +1408,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EntityId");
|
||||
|
||||
b.ToTable("Adm_T_CrudEndpoint", (string)null);
|
||||
});
|
||||
|
||||
|
|
@ -3769,163 +3764,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
|
|
@ -6399,17 +6237,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
.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 =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.City", null)
|
||||
|
|
@ -6500,17 +6327,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||
|
|
@ -6750,11 +6566,6 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("Skills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b =>
|
||||
{
|
||||
b.Navigation("Fields");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||
{
|
||||
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 {
|
||||
id: string;
|
||||
entityId: string;
|
||||
|
||||
tenantId: string;
|
||||
entityName: string;
|
||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
path: string;
|
||||
|
|
@ -67,7 +13,7 @@ export interface CrudEndpoint {
|
|||
}
|
||||
|
||||
export interface CreateUpdateCrudEndpointDto {
|
||||
entityId: string;
|
||||
tenantId: string;
|
||||
entityName: string;
|
||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
path: string;
|
||||
|
|
@ -78,6 +24,7 @@ export interface CreateUpdateCrudEndpointDto {
|
|||
|
||||
export interface CustomComponent {
|
||||
id: string;
|
||||
tenantId?: string;
|
||||
name: string;
|
||||
code: string;
|
||||
props?: string;
|
||||
|
|
@ -90,6 +37,7 @@ export interface CustomComponent {
|
|||
|
||||
export interface CustomComponentDto {
|
||||
id: string;
|
||||
tenantId?: string;
|
||||
name: string;
|
||||
code: string;
|
||||
props?: string;
|
||||
|
|
@ -101,6 +49,7 @@ export interface CustomComponentDto {
|
|||
}
|
||||
|
||||
export interface CreateUpdateCustomComponentDto {
|
||||
tenantId?: string;
|
||||
name: string;
|
||||
code: string;
|
||||
props?: string;
|
||||
|
|
@ -108,3 +57,20 @@ export interface CreateUpdateCustomComponentDto {
|
|||
isActive: boolean;
|
||||
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,
|
||||
CreateUpdateCrudEndpointDto,
|
||||
CreateUpdateCustomComponentDto,
|
||||
CreateUpdateSqlTableDto,
|
||||
CustomComponent,
|
||||
CustomComponentDto,
|
||||
SqlTable,
|
||||
} from '@/proxy/developerKit/models'
|
||||
|
||||
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
|
||||
async getCustomComponents(): Promise<PagedResultDto<CustomComponentDto>> {
|
||||
const response = await apiService.fetchData<PagedResultDto<CustomComponentDto>>({
|
||||
|
|
@ -121,14 +63,6 @@ class DeveloperKitService {
|
|||
}
|
||||
|
||||
// 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>> {
|
||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
||||
url: '/api/app/crud-endpoint-generate/active-endpoints',
|
||||
|
|
@ -137,9 +71,25 @@ class DeveloperKitService {
|
|||
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>>({
|
||||
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',
|
||||
})
|
||||
return response.data
|
||||
|
|
@ -180,38 +130,6 @@ class DeveloperKitService {
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
|||
import { developerKitService } from '@/services/developerKit.service'
|
||||
import type { DataSourceDto } from '@/proxy/data-source'
|
||||
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 { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
|
|
@ -62,25 +62,14 @@ const METHOD_COLOR: Record<string, string> = {
|
|||
const CrudEndpointManager: React.FC = () => {
|
||||
const { translate } = useLocalization()
|
||||
|
||||
// Local entity + endpoint state (no context dependency)
|
||||
const [entities, setEntities] = useState<SqlTable[]>([])
|
||||
// Endpoint state
|
||||
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(() => {
|
||||
refreshData()
|
||||
developerKitService
|
||||
.getGeneratedListEndpoints()
|
||||
.then((res) => setGeneratedEndpoints(res.items || []))
|
||||
.catch((err) => console.error('Failed to load endpoints', err))
|
||||
}, [])
|
||||
|
||||
// Data source + tables
|
||||
|
|
@ -135,19 +124,12 @@ const CrudEndpointManager: React.FC = () => {
|
|||
}, [selectedDataSource])
|
||||
|
||||
// Helpers
|
||||
const getEntityForTable = useCallback(
|
||||
(tableName: string): SqlTable | undefined =>
|
||||
entities.find((e) => e.tableName.toLowerCase() === tableName.toLowerCase()),
|
||||
[entities],
|
||||
)
|
||||
|
||||
const getEndpointsForTable = useCallback(
|
||||
(tableName: string): CrudEndpoint[] => {
|
||||
const entity = getEntityForTable(tableName)
|
||||
if (!entity) return []
|
||||
return generatedEndpoints.filter((ep) => ep.entityId === entity.id)
|
||||
const entityName = toPascalCase(tableName)
|
||||
return generatedEndpoints.filter((ep) => ep.entityName === entityName)
|
||||
},
|
||||
[entities, generatedEndpoints, getEntityForTable],
|
||||
[generatedEndpoints],
|
||||
)
|
||||
|
||||
const activeEndpointCount = (tableName: string) =>
|
||||
|
|
@ -181,30 +163,10 @@ const CrudEndpointManager: React.FC = () => {
|
|||
const key = table.fullName
|
||||
setGeneratingFor(key)
|
||||
try {
|
||||
let entity = getEntityForTable(table.tableName)
|
||||
if (!entity) {
|
||||
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)
|
||||
const entityName = toPascalCase(table.tableName)
|
||||
const result = await developerKitService.generateCrudEndpoints(entityName)
|
||||
setGeneratedEndpoints((prev) => [
|
||||
...prev.filter((ep) => ep.entityId !== entity!.id),
|
||||
...prev.filter((ep) => ep.entityName !== entityName),
|
||||
...(result.items || []),
|
||||
])
|
||||
} catch (err) {
|
||||
|
|
@ -352,7 +314,6 @@ const CrudEndpointManager: React.FC = () => {
|
|||
const tablesWithEndpoints = dbTables.filter((t) => allEndpointCount(t.tableName) > 0).length
|
||||
const totalActiveEndpoints = generatedEndpoints.filter((ep) => ep.isActive).length
|
||||
const selectedTableEndpoints = selectedTable ? getEndpointsForTable(selectedTable.tableName) : []
|
||||
const selectedEntity = selectedTable ? getEntityForTable(selectedTable.tableName) : undefined
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-4">
|
||||
|
|
@ -553,11 +514,6 @@ const CrudEndpointManager: React.FC = () => {
|
|||
<div>
|
||||
<h4 className="text-slate-900">{selectedTable.schemaName}.{selectedTable.tableName}</h4>
|
||||
</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 className="flex items-center gap-2">
|
||||
{selectedTableEndpoints.length > 0 && (
|
||||
|
|
@ -598,9 +554,7 @@ const CrudEndpointManager: React.FC = () => {
|
|||
<FaBolt className="text-3xl mb-3 text-slate-200" />
|
||||
<p className="font-medium">Henuz endpoint yok</p>
|
||||
<p className="text-sm mt-1">
|
||||
{selectedEntity
|
||||
? '"CRUD Endpoint Olustur" butonuna tiklayin'
|
||||
: 'Bu tablo icin Sql Table otomatik olusturulacak ve 5 endpoint uretilecektir'}
|
||||
"CRUD Endpoint Olustur" butonuna tiklayin
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -655,22 +609,6 @@ const CrudEndpointManager: React.FC = () => {
|
|||
</code>
|
||||
</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 */}
|
||||
<button
|
||||
onClick={() => setExpandedEndpoint(isExpanded ? null : ep.id)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useCallback, useMemo, useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
|
||||
import {
|
||||
FaPlus,
|
||||
|
|
@ -8,7 +9,6 @@ import {
|
|||
FaTable,
|
||||
FaCloudUploadAlt,
|
||||
FaCheck,
|
||||
FaChevronRight,
|
||||
FaLink,
|
||||
FaEdit,
|
||||
FaTimes,
|
||||
|
|
@ -16,9 +16,8 @@ import {
|
|||
} from 'react-icons/fa'
|
||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.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 { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
||||
|
||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -55,11 +54,6 @@ interface TableSettings {
|
|||
menuPrefix: string
|
||||
entityName: string
|
||||
tableName: string
|
||||
displayName: string
|
||||
description: string
|
||||
isActive: boolean
|
||||
isFullAudited: boolean
|
||||
isMultiTenant: boolean
|
||||
}
|
||||
|
||||
interface TableDesignerDialogProps {
|
||||
|
|
@ -70,23 +64,6 @@ interface TableDesignerDialogProps {
|
|||
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 ────────────────────────────────────────────────────────────────
|
||||
|
||||
const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
||||
|
|
@ -104,6 +81,15 @@ const DATA_TYPES: { value: SqlDataType; label: string }[] = [
|
|||
]
|
||||
|
||||
const FULL_AUDIT_COLUMNS: ColumnDefinition[] = [
|
||||
{
|
||||
id: '__Id',
|
||||
columnName: 'Id',
|
||||
dataType: 'uniqueidentifier',
|
||||
maxLength: '',
|
||||
isNullable: false,
|
||||
defaultValue: 'NEWID()',
|
||||
description: 'Primary key',
|
||||
},
|
||||
{
|
||||
id: '__CreationTime',
|
||||
columnName: 'CreationTime',
|
||||
|
|
@ -181,13 +167,13 @@ const CASCADE_OPTIONS: { value: CascadeBehavior; label: string }[] = [
|
|||
{ value: 'Restrict', label: 'Restrict' },
|
||||
]
|
||||
|
||||
const EMPTY_FK: Omit<FkRelationshipDefinition, 'id'> = {
|
||||
const EMPTY_FK: Omit<SqlTableRelation, 'id'> = {
|
||||
relationshipType: 'OneToMany',
|
||||
fkColumnName: '',
|
||||
referencedTable: '',
|
||||
referencedColumn: 'Id',
|
||||
cascadeDelete: 'NoAction',
|
||||
cascadeUpdate: 'NoAction',
|
||||
cascadeUpdate: 'Cascade',
|
||||
isRequired: false,
|
||||
description: '',
|
||||
}
|
||||
|
|
@ -227,21 +213,17 @@ function colToSqlLine(col: ColumnDefinition, addComma = true): string {
|
|||
function generateCreateTableSql(
|
||||
columns: ColumnDefinition[],
|
||||
settings: TableSettings,
|
||||
relationships: FkRelationshipDefinition[],
|
||||
relationships: SqlTableRelation[],
|
||||
): string {
|
||||
const tableName = settings.tableName || 'NewTable'
|
||||
const fullTableName = `[dbo].[${tableName}]`
|
||||
|
||||
const userCols = columns.filter((c) => c.columnName.trim())
|
||||
const auditCols = settings.isFullAudited ? FULL_AUDIT_COLUMNS : []
|
||||
const tenantCols = settings.isMultiTenant ? [TENANT_COLUMN] : []
|
||||
const allBodyCols = [...userCols, ...auditCols, ...tenantCols]
|
||||
const allBodyCols = userCols
|
||||
|
||||
// When Full Audited: auto-add Id (PK) + audit cols
|
||||
// When not Full Audited: user is responsible for their own Id column
|
||||
const bodyLines = settings.isFullAudited
|
||||
const hasIdCol = userCols.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
||||
const bodyLines = hasIdCol
|
||||
? [
|
||||
` [Id] uniqueidentifier NOT NULL DEFAULT NEWID(),`,
|
||||
...allBodyCols.map((c) => colToSqlLine(c, true)),
|
||||
` CONSTRAINT [PK_${tableName}] PRIMARY KEY NONCLUSTERED ([Id])`,
|
||||
]
|
||||
|
|
@ -266,8 +248,6 @@ function generateCreateTableSql(
|
|||
const lines: string[] = [
|
||||
`/* ── Table: ${fullTableName} ── */`,
|
||||
...(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 */'] : []),
|
||||
'',
|
||||
`CREATE TABLE ${fullTableName}`,
|
||||
|
|
@ -334,8 +314,8 @@ function generateAlterTableSql(
|
|||
originalCols: ColumnDefinition[],
|
||||
currentCols: ColumnDefinition[],
|
||||
tableName: string,
|
||||
relationships: FkRelationshipDefinition[],
|
||||
originalRelationships: FkRelationshipDefinition[],
|
||||
relationships: SqlTableRelation[],
|
||||
originalRelationships: SqlTableRelation[],
|
||||
): string {
|
||||
const fullTableName = `[dbo].[${tableName}]`
|
||||
const lines: string[] = [`/* ── ALTER TABLE: ${fullTableName} ── */`, '']
|
||||
|
|
@ -412,7 +392,7 @@ function generateAlterTableSql(
|
|||
const fkCascadeSql = (b: CascadeBehavior) =>
|
||||
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}`
|
||||
lines.push(`-- 🔗 FK Ekle: [${rel.fkColumnName}] → [${rel.referencedTable}]`)
|
||||
lines.push(`ALTER TABLE ${fullTableName}`)
|
||||
|
|
@ -424,9 +404,9 @@ function generateAlterTableSql(
|
|||
lines.push('')
|
||||
}
|
||||
|
||||
const origRelById = new Map<string, FkRelationshipDefinition>()
|
||||
const origRelById = new Map<string, SqlTableRelation>()
|
||||
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))
|
||||
|
||||
// Removed FKs → DROP CONSTRAINT
|
||||
|
|
@ -478,18 +458,6 @@ function generateAlterTableSql(
|
|||
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
|
||||
type Step = 0 | 1 | 2 | 3
|
||||
|
||||
|
|
@ -508,11 +476,6 @@ const DEFAULT_SETTINGS: TableSettings = {
|
|||
menuPrefix: '',
|
||||
entityName: '',
|
||||
tableName: '',
|
||||
displayName: '',
|
||||
description: '',
|
||||
isActive: true,
|
||||
isFullAudited: true,
|
||||
isMultiTenant: false,
|
||||
}
|
||||
|
||||
// ─── Component ────────────────────────────────────────────────────────────────
|
||||
|
|
@ -536,12 +499,12 @@ const SqlTableDesignerDialog = ({
|
|||
const [settings, setSettings] = useState<TableSettings>(DEFAULT_SETTINGS)
|
||||
const [menuOptions, setMenuOptions] = useState<MenuOption[]>([])
|
||||
const [menuLoading, setMenuLoading] = useState(false)
|
||||
const [relationships, setRelationships] = useState<FkRelationshipDefinition[]>([])
|
||||
const [originalRelationships, setOriginalRelationships] = useState<FkRelationshipDefinition[]>([])
|
||||
const [relationships, setRelationships] = useState<SqlTableRelation[]>([])
|
||||
const [originalRelationships, setOriginalRelationships] = useState<SqlTableRelation[]>([])
|
||||
const [fksLoading, setFksLoading] = useState(false)
|
||||
const [fkModalOpen, setFkModalOpen] = useState(false)
|
||||
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 [targetTableColumns, setTargetTableColumns] = useState<string[]>([])
|
||||
const [targetColsLoading, setTargetColsLoading] = useState(false)
|
||||
|
|
@ -583,42 +546,17 @@ const SqlTableDesignerDialog = ({
|
|||
.catch(() => {})
|
||||
.finally(() => setColsLoading(false))
|
||||
|
||||
// Load SqlTable metadata (menu, entityName, displayName, flags…)
|
||||
developerKitService
|
||||
.getSqlTables()
|
||||
.then((res) => {
|
||||
const match = (res.items ?? []).find(
|
||||
(e) => e.tableName.toLowerCase() === initialTableData.tableName.toLowerCase(),
|
||||
)
|
||||
if (match) {
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
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(() => {})
|
||||
// Derive settings from table name (e.g. "Sas_D_EntityName" → menu: "Sas", entity: "EntityName")
|
||||
const parts = initialTableData.tableName.split('_')
|
||||
const derivedMenu = parts[0] ?? ''
|
||||
const derivedEntity = parts[parts.length - 1] ?? initialTableData.tableName
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
menuValue: derivedMenu,
|
||||
menuPrefix: derivedMenu,
|
||||
entityName: derivedEntity,
|
||||
displayName: derivedEntity,
|
||||
}))
|
||||
|
||||
// Load existing FK constraints
|
||||
const fkQuery = [
|
||||
|
|
@ -649,7 +587,7 @@ const SqlTableDesignerDialog = ({
|
|||
.executeQuery({ queryText: fkQuery, dataSourceCode: dataSource })
|
||||
.then((res) => {
|
||||
const rows: any[] = res.data?.data ?? []
|
||||
const fkDefs: FkRelationshipDefinition[] = rows.map((r) => ({
|
||||
const fkDefs: SqlTableRelation[] = rows.map((r) => ({
|
||||
id: crypto.randomUUID(),
|
||||
constraintName: r.constraintName,
|
||||
relationshipType: 'OneToMany' as RelationshipType,
|
||||
|
|
@ -687,6 +625,29 @@ const SqlTableDesignerDialog = ({
|
|||
|
||||
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 moveColumn = (id: string, direction: 'up' | 'down') => {
|
||||
|
|
@ -759,7 +720,7 @@ const SqlTableDesignerDialog = ({
|
|||
setFkModalOpen(true)
|
||||
}
|
||||
|
||||
const openEditFk = (rel: FkRelationshipDefinition) => {
|
||||
const openEditFk = (rel: SqlTableRelation) => {
|
||||
setEditingFkId(rel.id)
|
||||
const { id: _id, ...rest } = rel
|
||||
setFkForm(rest)
|
||||
|
|
@ -801,10 +762,7 @@ const SqlTableDesignerDialog = ({
|
|||
const baseOk =
|
||||
!!settings.tableName.trim() && !!settings.entityName.trim() && !!settings.menuValue
|
||||
if (!baseOk) return false
|
||||
if (!settings.isFullAudited) {
|
||||
return columns.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
||||
}
|
||||
return true
|
||||
return columns.some((c) => c.columnName.trim().toLowerCase() === 'id')
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -818,80 +776,6 @@ const SqlTableDesignerDialog = ({
|
|||
|
||||
// ── 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 () => {
|
||||
if (!dataSource) {
|
||||
toast.push(
|
||||
|
|
@ -910,8 +794,6 @@ const SqlTableDesignerDialog = ({
|
|||
})
|
||||
if (result.data.success) {
|
||||
const deployedTable = settings.tableName || initialTableData?.tableName || ''
|
||||
// Sync entity metadata to SqlTable / SqlTableField tables
|
||||
await syncSqlTableMetadata(deployedTable)
|
||||
toast.push(
|
||||
<Notification type="success" title="Basarili">
|
||||
Tablo basariyla {isEditMode ? 'güncellendi' : 'oluşturuldu'}: [dbo].[{deployedTable}]
|
||||
|
|
@ -996,31 +878,27 @@ const SqlTableDesignerDialog = ({
|
|||
)}
|
||||
{!colsLoading && (
|
||||
<>
|
||||
<div className="flex items-center justify-between py-3">
|
||||
{isEditMode ? (
|
||||
<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">
|
||||
📝 Mevcut sütunlar yüklendi. Değişiklikler T-SQL Önizleme adımında <strong>ALTER TABLE</strong> olarak gösterilecek.
|
||||
|
|
||||
<span className="ml-1">
|
||||
<span className="inline-block w-2.5 h-2.5 rounded bg-green-200 border border-green-400 mr-0.5"></span>Yeni
|
||||
<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)
|
||||
<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
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<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">
|
||||
Sonraki adımda seceğiniz{' '}
|
||||
<strong>Full Audited Entity</strong> / <strong>Multi-Tenant</strong> ayarlarina gore ek
|
||||
sutunlar da dahil edilecektir.
|
||||
</div>
|
||||
)}
|
||||
<Button size="xs" variant="solid" color="blue-600" icon={<FaPlus />} onClick={addColumn}>
|
||||
Ekle
|
||||
</Button>
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
|
||||
Full Audited Entity Sütunları Ekle
|
||||
</Button>
|
||||
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
||||
MultiTenant Sütunları Ekle
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button size="xs" variant="solid" color="red-600" icon={<FaTrash />} onClick={clearAllColumns}>
|
||||
Tüm Sütunları Sil
|
||||
</Button>
|
||||
<Button size="xs" variant="solid" color="blue-600" icon={<FaPlus />} onClick={addColumn}>
|
||||
Sütun Ekle
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 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">Veri Tipi *</div>
|
||||
<div className="col-span-1 text-center">Max</div>
|
||||
|
|
@ -1032,7 +910,7 @@ const SqlTableDesignerDialog = ({
|
|||
|
||||
{/* Editable column rows */}
|
||||
{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:{' '}
|
||||
{[...duplicateColumnNames].map((n) => (
|
||||
<code key={n} className="bg-red-100 dark:bg-red-900/40 px-1 rounded mr-1">
|
||||
|
|
@ -1079,7 +957,7 @@ const SqlTableDesignerDialog = ({
|
|||
return (
|
||||
<div
|
||||
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">
|
||||
<input
|
||||
|
|
@ -1174,8 +1052,8 @@ const SqlTableDesignerDialog = ({
|
|||
})}
|
||||
</div>
|
||||
|
||||
{/* Id warning when Full Audited is off */}
|
||||
{!isEditMode && !settings.isFullAudited &&
|
||||
{/* Id warning */}
|
||||
{!isEditMode &&
|
||||
!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">
|
||||
⚠️ <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="grid grid-cols-2 gap-2">
|
||||
{/* Menu Name */}
|
||||
<div>
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium mb-1">
|
||||
Menu Name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
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}
|
||||
disabled={menuLoading}
|
||||
disabled={menuLoading || isEditMode} // Menu cannot be changed in edit mode (as it determines the table name)
|
||||
onChange={(e) => onMenuChange(e.target.value)}
|
||||
>
|
||||
<option value=""></option>
|
||||
|
|
@ -1221,6 +1099,7 @@ const SqlTableDesignerDialog = ({
|
|||
</label>
|
||||
<input
|
||||
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"
|
||||
value={settings.entityName}
|
||||
onChange={(e) => onEntityNameChange(e.target.value)}
|
||||
|
|
@ -1238,101 +1117,12 @@ const SqlTableDesignerDialog = ({
|
|||
value={isEditMode ? (initialTableData?.tableName ?? '') : settings.tableName}
|
||||
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>
|
||||
|
||||
{/* 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 */}
|
||||
<div className="flex items-start gap-3">
|
||||
<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 &&
|
||||
{/* Warning: no Id column */}
|
||||
{!isEditMode &&
|
||||
!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">
|
||||
⛔ <strong>Full Audited Entity</strong> seçili değil. Geri dönüp{' '}
|
||||
|
|
@ -1433,7 +1223,7 @@ const SqlTableDesignerDialog = ({
|
|||
</>}
|
||||
|
||||
{/* 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="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-lg flex flex-col">
|
||||
{/* Modal Header */}
|
||||
|
|
@ -1484,7 +1274,7 @@ const SqlTableDesignerDialog = ({
|
|||
<select
|
||||
value={fkForm.fkColumnName}
|
||||
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>
|
||||
{columns
|
||||
|
|
@ -1507,12 +1297,12 @@ const SqlTableDesignerDialog = ({
|
|||
setFkForm((f) => ({ ...f, referencedTable: val, referencedColumn: '' }))
|
||||
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>
|
||||
{dbTables.map((t) => (
|
||||
<option key={`${t.schemaName}.${t.tableName}`} value={t.tableName}>
|
||||
[{t.schemaName}].[{t.tableName}]
|
||||
{t.tableName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
@ -1527,7 +1317,7 @@ const SqlTableDesignerDialog = ({
|
|||
value={fkForm.referencedColumn}
|
||||
onChange={(e) => setFkForm((f) => ({ ...f, referencedColumn: e.target.value }))}
|
||||
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>
|
||||
{targetTableColumns.map((col) => (
|
||||
|
|
@ -1619,7 +1409,8 @@ const SqlTableDesignerDialog = ({
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -1639,7 +1430,7 @@ const SqlTableDesignerDialog = ({
|
|||
Kopyala
|
||||
</button>
|
||||
</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}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue