Report Template Editor güncellemesi

This commit is contained in:
Sedat ÖZTÜRK 2025-08-15 12:19:20 +03:00
parent 5b6b7acf19
commit b6efa5c3fd
27 changed files with 7730 additions and 46 deletions

View file

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
public class CreateReportTemplateDto
{
[Required]
[StringLength(256)]
public string Name { get; set; }
[StringLength(1000)]
public string Description { get; set; }
[Required]
public string HtmlContent { get; set; }
[StringLength(100)]
public string Category { get; set; }
public List<string> Tags { get; set; }
public List<CreateReportParameterDto> Parameters { get; set; }
public CreateReportTemplateDto()
{
Tags = new List<string>();
Parameters = new List<CreateReportParameterDto>();
}
}
public class CreateReportParameterDto
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(200)]
public string Placeholder { get; set; }
public ReportParameterType Type { get; set; }
[StringLength(500)]
public string DefaultValue { get; set; }
public bool Required { get; set; }
[StringLength(1000)]
public string Description { get; set; }
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
public class GenerateReportDto
{
[Required]
public Guid TemplateId { get; set; }
public Dictionary<string, string> Parameters { get; set; }
public GenerateReportDto()
{
Parameters = new Dictionary<string, string>();
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Reports
{
public class GeneratedReportDto : FullAuditedEntityDto<Guid>
{
public Guid? TemplateId { get; set; }
[Required]
[StringLength(256)]
public string TemplateName { get; set; }
[Required]
public string GeneratedContent { get; set; }
public Dictionary<string, string> Parameters { get; set; }
public DateTime GeneratedAt { get; set; }
public ReportTemplateDto Template { get; set; }
public GeneratedReportDto()
{
Parameters = new Dictionary<string, string>();
GeneratedAt = DateTime.UtcNow;
}
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Reports
{
public class GetReportTemplatesInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public string Category { get; set; }
}
public class GetGeneratedReportsInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public Guid? TemplateId { get; set; }
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Kurs.Platform.Reports
{
public interface IReportAppService : IApplicationService
{
// Template operations
Task<PagedResultDto<ReportTemplateDto>> GetTemplatesAsync(GetReportTemplatesInput input);
Task<ReportTemplateDto> GetTemplateAsync(Guid id);
Task<ReportTemplateDto> CreateTemplateAsync(CreateReportTemplateDto input);
Task<ReportTemplateDto> UpdateTemplateAsync(Guid id, UpdateReportTemplateDto input);
Task DeleteTemplateAsync(Guid id);
// Generated Report operations
Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input);
Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id);
Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input);
Task DeleteGeneratedReportAsync(Guid id);
// Bulk operations
Task<ReportsDataDto> GetAllDataAsync();
}
public class ReportsDataDto
{
public PagedResultDto<ReportTemplateDto> Templates { get; set; }
public PagedResultDto<GeneratedReportDto> GeneratedReports { get; set; }
}
}

View file

@ -0,0 +1,29 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
public class ReportParameterDto
{
public Guid Id { get; set; }
public Guid ReportTemplateId { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(200)]
public string Placeholder { get; set; }
public ReportParameterType Type { get; set; }
[StringLength(500)]
public string DefaultValue { get; set; }
public bool Required { get; set; }
[StringLength(1000)]
public string Description { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using System;
namespace Kurs.Platform.Reports
{
public enum ReportParameterType
{
Text = 0,
Number = 1,
Date = 2,
Email = 3,
Url = 4
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Reports
{
public class ReportTemplateDto : FullAuditedEntityDto<Guid>
{
[Required]
[StringLength(256)]
public string Name { get; set; }
[StringLength(1000)]
public string Description { get; set; }
[Required]
public string HtmlContent { get; set; }
[StringLength(100)]
public string Category { get; set; }
public List<string> Tags { get; set; }
public List<ReportParameterDto> Parameters { get; set; }
public ReportTemplateDto()
{
Tags = new List<string>();
Parameters = new List<ReportParameterDto>();
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
public class UpdateReportTemplateDto
{
[Required]
[StringLength(256)]
public string Name { get; set; }
[StringLength(1000)]
public string Description { get; set; }
[Required]
public string HtmlContent { get; set; }
[StringLength(100)]
public string Category { get; set; }
public List<string> Tags { get; set; }
public List<UpdateReportParameterDto> Parameters { get; set; }
public UpdateReportTemplateDto()
{
Tags = new List<string>();
Parameters = new List<UpdateReportParameterDto>();
}
}
public class UpdateReportParameterDto
{
public Guid? Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(200)]
public string Placeholder { get; set; }
public ReportParameterType Type { get; set; }
[StringLength(500)]
public string DefaultValue { get; set; }
public bool Required { get; set; }
[StringLength(1000)]
public string Description { get; set; }
}
}

View file

@ -0,0 +1,276 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Kurs.Platform.Entities;
using Kurs.Platform.Repositories;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Reports;
[Authorize()]
public class ReportAppService : PlatformAppService, IReportAppService
{
private readonly IReportTemplateRepository _reportTemplateRepository;
private readonly IGeneratedReportRepository _generatedReportRepository;
private readonly IRepository<ReportParameter, Guid> _reportParameterRepository;
public ReportAppService(
IReportTemplateRepository reportTemplateRepository,
IGeneratedReportRepository generatedReportRepository,
IRepository<ReportParameter, Guid> reportParameterRepository)
{
_reportTemplateRepository = reportTemplateRepository;
_generatedReportRepository = generatedReportRepository;
_reportParameterRepository = reportParameterRepository;
}
public async Task<PagedResultDto<ReportTemplateDto>> GetTemplatesAsync(GetReportTemplatesInput input)
{
var totalCount = await _reportTemplateRepository.GetCountAsync(input.Filter, input.Category);
var templates = await _reportTemplateRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.Category);
var templateDtos = templates.Select(MapToReportTemplateDto).ToList();
return new PagedResultDto<ReportTemplateDto>(totalCount, templateDtos);
}
public async Task<ReportTemplateDto> GetTemplateAsync(Guid id)
{
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(id);
return MapToReportTemplateDto(template);
}
public async Task<ReportTemplateDto> CreateTemplateAsync(CreateReportTemplateDto input)
{
var template = new ReportTemplate(
GuidGenerator.Create(),
input.Name,
input.Description,
input.HtmlContent,
input.Category ?? "Genel");
template.Tags = JsonSerializer.Serialize(input.Tags);
template = await _reportTemplateRepository.InsertAsync(template, true);
// Parameters ekle
foreach (var paramDto in input.Parameters)
{
var parameter = new ReportParameter(
GuidGenerator.Create(),
template.Id,
paramDto.Name,
paramDto.Placeholder,
(Entities.ReportParameterType)paramDto.Type,
paramDto.Required);
parameter.DefaultValue = paramDto.DefaultValue;
parameter.Description = paramDto.Description;
await _reportParameterRepository.InsertAsync(parameter);
}
return await GetTemplateAsync(template.Id);
}
public async Task<ReportTemplateDto> UpdateTemplateAsync(Guid id, UpdateReportTemplateDto input)
{
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(id);
template.Name = input.Name;
template.Description = input.Description;
template.HtmlContent = input.HtmlContent;
template.Category = input.Category ?? "Genel";
template.Tags = JsonSerializer.Serialize(input.Tags);
await _reportTemplateRepository.UpdateAsync(template);
// Mevcut parametreleri sil
var existingParameters = await _reportParameterRepository.GetListAsync(p => p.ReportTemplateId == id);
foreach (var param in existingParameters)
{
await _reportParameterRepository.DeleteAsync(param);
}
// Yeni parametreleri ekle
foreach (var paramDto in input.Parameters)
{
var parameter = new ReportParameter(
paramDto.Id ?? GuidGenerator.Create(),
template.Id,
paramDto.Name,
paramDto.Placeholder,
(Entities.ReportParameterType)paramDto.Type,
paramDto.Required);
parameter.DefaultValue = paramDto.DefaultValue;
parameter.Description = paramDto.Description;
await _reportParameterRepository.InsertAsync(parameter);
}
return await GetTemplateAsync(template.Id);
}
public async Task DeleteTemplateAsync(Guid id)
{
await _reportTemplateRepository.DeleteAsync(id);
}
public async Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input)
{
var totalCount = await _generatedReportRepository.GetCountAsync(input.Filter, input.TemplateId);
var reports = await _generatedReportRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.TemplateId);
var reportDtos = reports.Select(MapToGeneratedReportDto).ToList();
return new PagedResultDto<GeneratedReportDto>(totalCount, reportDtos);
}
public async Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id)
{
var report = await _generatedReportRepository.GetByIdWithTemplateAsync(id);
return MapToGeneratedReportDto(report);
}
public async Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input)
{
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(input.TemplateId);
if (template == null)
{
throw new ArgumentException("Template not found");
}
// HTML içeriğindeki parametreleri değiştir
var generatedContent = template.HtmlContent;
foreach (var param in input.Parameters)
{
var pattern = $"@@{param.Key}";
generatedContent = generatedContent.Replace(pattern, param.Value ?? "");
}
var generatedReport = new GeneratedReport(
GuidGenerator.Create(),
template.Id,
template.Name,
generatedContent,
JsonSerializer.Serialize(input.Parameters));
generatedReport = await _generatedReportRepository.InsertAsync(generatedReport, true);
return await GetGeneratedReportAsync(generatedReport.Id);
}
public async Task DeleteGeneratedReportAsync(Guid id)
{
await _generatedReportRepository.DeleteAsync(id);
}
public async Task<ReportsDataDto> GetAllDataAsync()
{
var templatesInput = new GetReportTemplatesInput { MaxResultCount = 1000 };
var reportsInput = new GetGeneratedReportsInput { MaxResultCount = 1000 };
var templates = await GetTemplatesAsync(templatesInput);
var reports = await GetGeneratedReportsAsync(reportsInput);
return new ReportsDataDto
{
Templates = templates,
GeneratedReports = reports
};
}
private ReportTemplateDto MapToReportTemplateDto(ReportTemplate template)
{
var dto = new ReportTemplateDto
{
Id = template.Id,
Name = template.Name,
Description = template.Description,
HtmlContent = template.HtmlContent,
Category = template.Category,
CreationTime = template.CreationTime,
LastModificationTime = template.LastModificationTime,
CreatorId = template.CreatorId,
LastModifierId = template.LastModifierId
};
// Tags deserialize
try
{
dto.Tags = string.IsNullOrEmpty(template.Tags)
? new List<string>()
: JsonSerializer.Deserialize<List<string>>(template.Tags);
}
catch
{
dto.Tags = new List<string>();
}
// Parameters map
dto.Parameters = template.Parameters?.Select(p => new ReportParameterDto
{
Id = p.Id,
ReportTemplateId = p.ReportTemplateId,
Name = p.Name,
Placeholder = p.Placeholder,
Type = (Reports.ReportParameterType)p.Type,
DefaultValue = p.DefaultValue,
Required = p.Required,
Description = p.Description
}).ToList() ?? new List<ReportParameterDto>();
return dto;
}
private GeneratedReportDto MapToGeneratedReportDto(GeneratedReport report)
{
var dto = new GeneratedReportDto
{
Id = report.Id,
TemplateId = report.TemplateId,
TemplateName = report.TemplateName,
GeneratedContent = report.GeneratedContent,
GeneratedAt = report.GeneratedAt,
CreationTime = report.CreationTime,
LastModificationTime = report.LastModificationTime,
CreatorId = report.CreatorId,
LastModifierId = report.LastModifierId
};
// Parameters deserialize
try
{
dto.Parameters = string.IsNullOrEmpty(report.Parameters)
? new Dictionary<string, string>()
: JsonSerializer.Deserialize<Dictionary<string, string>>(report.Parameters);
}
catch
{
dto.Parameters = new Dictionary<string, string>();
}
// Template mapping
if (report.Template != null)
{
dto.Template = MapToReportTemplateDto(report.Template);
}
return dto;
}
}

View file

@ -0,0 +1,91 @@
using AutoMapper;
using Kurs.Platform.Entities;
using System.Text.Json;
using System.Collections.Generic;
namespace Kurs.Platform.Reports
{
public class ReportAutoMapperProfile : Profile
{
public ReportAutoMapperProfile()
{
CreateMap<ReportTemplate, ReportTemplateDto>()
.ForMember(dest => dest.Tags, opt => opt.MapFrom(src =>
ConvertTagsFromJson(src.Tags)))
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => src.Parameters));
CreateMap<CreateReportTemplateDto, ReportTemplate>()
.ForMember(dest => dest.Tags, opt => opt.MapFrom(src => ConvertTagsToJson(src.Tags)))
.ForMember(dest => dest.Parameters, opt => opt.Ignore());
CreateMap<UpdateReportTemplateDto, ReportTemplate>()
.ForMember(dest => dest.Tags, opt => opt.MapFrom(src => ConvertTagsToJson(src.Tags)))
.ForMember(dest => dest.Parameters, opt => opt.Ignore());
CreateMap<ReportParameter, ReportParameterDto>()
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => (ReportParameterType)src.Type));
CreateMap<CreateReportParameterDto, ReportParameter>()
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => (Entities.ReportParameterType)src.Type))
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore());
CreateMap<UpdateReportParameterDto, ReportParameter>()
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => (Entities.ReportParameterType)src.Type))
.ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore());
CreateMap<GeneratedReport, GeneratedReportDto>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src =>
ConvertParametersFromJson(src.Parameters)))
.ForMember(dest => dest.Template, opt => opt.MapFrom(src => src.Template));
CreateMap<GenerateReportDto, GeneratedReport>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => ConvertParametersToJson(src.Parameters)))
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.TemplateId, opt => opt.MapFrom(src => src.TemplateId))
.ForMember(dest => dest.TemplateName, opt => opt.Ignore())
.ForMember(dest => dest.GeneratedContent, opt => opt.Ignore())
.ForMember(dest => dest.GeneratedAt, opt => opt.Ignore());
}
private static List<string> ConvertTagsFromJson(string tags)
{
if (string.IsNullOrEmpty(tags))
return new List<string>();
try
{
return JsonSerializer.Deserialize<List<string>>(tags) ?? new List<string>();
}
catch
{
return new List<string>();
}
}
private static string ConvertTagsToJson(List<string> tags)
{
return JsonSerializer.Serialize(tags ?? new List<string>());
}
private static Dictionary<string, string> ConvertParametersFromJson(string parameters)
{
if (string.IsNullOrEmpty(parameters))
return new Dictionary<string, string>();
try
{
return JsonSerializer.Deserialize<Dictionary<string, string>>(parameters) ?? new Dictionary<string, string>();
}
catch
{
return new Dictionary<string, string>();
}
}
private static string ConvertParametersToJson(Dictionary<string, string> parameters)
{
return JsonSerializer.Serialize(parameters ?? new Dictionary<string, string>());
}
}
}

View file

@ -5,8 +5,8 @@ using Kurs.Platform.Entities;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Routes
{
namespace Kurs.Platform.Routes;
public class RouteAppService : PlatformAppService
{
private readonly IRepository<Route, Guid> _routeRepository;
@ -27,4 +27,3 @@ namespace Kurs.Platform.Routes
return ObjectMapper.Map<List<Route>, List<RouteDto>>(items);
}
}
}

View file

@ -1261,6 +1261,12 @@
"en": "Menu Management",
"tr": "Menü Yönetimi"
},
{
"resourceName": "Platform",
"key": "App.Reports",
"en": "Report Management",
"tr": "Rapor Yönetimi"
},
{
"resourceName": "Platform",
"key": "App.Menus.Menu",
@ -10083,6 +10089,16 @@
"RequiredPermissionName": "App.DeveloperKit",
"IsDisabled": false
},
{
"ParentCode": "App.Saas",
"Code": "App.Reports",
"DisplayName": "App.Reports",
"Order": 14,
"Url": "/admin/reports",
"Icon": "FcDocument",
"RequiredPermissionName": "App.Reports",
"IsDisabled": false
},
{
"ParentCode": null,
"Code": "App.Administration",
@ -10307,7 +10323,7 @@
"ParentCode": "App.Saas",
"Code": "App.Orders",
"DisplayName": "App.Orders",
"Order": 14,
"Order": 15,
"Url": null,
"Icon": "FcEndCall",
"RequiredPermissionName": null,
@ -10431,6 +10447,10 @@
"Name": "App.DeveloperKit",
"DisplayName": "App.DeveloperKit"
},
{
"Name": "App.Reports",
"DisplayName": "App.Reports"
},
{
"Name": "App.Orders",
"DisplayName": "App.Orders"
@ -12652,6 +12672,55 @@
"DisplayName": "Import",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports",
"ParentName": null,
"DisplayName": "App.Reports",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports.Create",
"ParentName": "App.Reports",
"DisplayName": "Create",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports.Delete",
"ParentName": "App.Reports",
"DisplayName": "Delete",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports.Export",
"ParentName": "App.Reports",
"DisplayName": "Export",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports.Import",
"ParentName": "App.Reports",
"DisplayName": "Import",
"IsEnabled": true,
"MultiTenancySide": 2
},
{
"GroupName": "App.Reports",
"Name": "App.Reports.Update",
"ParentName": "App.Reports",
"DisplayName": "Update",
"IsEnabled": true,
"MultiTenancySide": 2
}
],
"Sectors": [
@ -25581,6 +25650,20 @@
"componentPath": "@/views/developerKit/CodePage",
"routeType": "protected",
"authority": ["App.DeveloperKit.Components"]
},
{
"key": "admin.reports",
"path": "/admin/reports",
"componentPath": "@/views/report/DashboardPage",
"routeType": "protected",
"authority": ["App.Reports"]
},
{
"key": "admin.reports.view",
"path": "/admin/reports/:id",
"componentPath": "@/views/report/ReportViewerPage",
"routeType": "protected",
"authority": ["App.Reports.Update"]
}
],
"CustomEndpoints": [

View file

@ -0,0 +1,45 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities.Auditing;
namespace Kurs.Platform.Entities
{
public class GeneratedReport : FullAuditedAggregateRoot<Guid>
{
public Guid? TemplateId { get; set; }
[Required]
[StringLength(256)]
public string TemplateName { get; set; }
[Required]
public string GeneratedContent { get; set; }
public string Parameters { get; set; } // JSON string for parameters
public DateTime GeneratedAt { get; set; }
// Navigation property
public virtual ReportTemplate Template { get; set; }
public GeneratedReport()
{
GeneratedAt = DateTime.UtcNow;
}
public GeneratedReport(
Guid id,
Guid? templateId,
string templateName,
string generatedContent,
string parameters
) : base(id)
{
TemplateId = templateId;
TemplateName = templateName;
GeneratedContent = generatedContent;
Parameters = parameters;
GeneratedAt = DateTime.UtcNow;
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities.Auditing;
namespace Kurs.Platform.Entities
{
public enum ReportParameterType
{
Text = 0,
Number = 1,
Date = 2,
Email = 3,
Url = 4
}
public class ReportParameter : FullAuditedEntity<Guid>
{
public Guid ReportTemplateId { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(200)]
public string Placeholder { get; set; }
public ReportParameterType Type { get; set; }
[StringLength(500)]
public string DefaultValue { get; set; }
public bool Required { get; set; }
[StringLength(1000)]
public string Description { get; set; }
// Navigation property
public virtual ReportTemplate ReportTemplate { get; set; }
public ReportParameter()
{
}
public ReportParameter(
Guid id,
Guid reportTemplateId,
string name,
string placeholder,
ReportParameterType type,
bool required = false
) : base(id)
{
ReportTemplateId = reportTemplateId;
Name = name;
Placeholder = placeholder;
Type = type;
Required = required;
}
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities.Auditing;
namespace Kurs.Platform.Entities
{
public class ReportTemplate : FullAuditedAggregateRoot<Guid>
{
[Required]
[StringLength(256)]
public string Name { get; set; }
[StringLength(1000)]
public string Description { get; set; }
[Required]
public string HtmlContent { get; set; }
[StringLength(100)]
public string Category { get; set; }
public string Tags { get; set; } // JSON string array
// Navigation property
public virtual ICollection<ReportParameter> Parameters { get; set; }
public ReportTemplate()
{
Parameters = new List<ReportParameter>();
}
public ReportTemplate(
Guid id,
string name,
string description,
string htmlContent,
string category = "Genel"
) : base(id)
{
Name = name;
Description = description;
HtmlContent = htmlContent;
Category = category;
Parameters = new List<ReportParameter>();
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kurs.Platform.Entities;
using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Repositories
{
public interface IGeneratedReportRepository : IRepository<GeneratedReport, Guid>
{
Task<List<GeneratedReport>> GetListAsync(
int skipCount = 0,
int maxResultCount = int.MaxValue,
string sorting = null,
string filter = null,
Guid? templateId = null
);
Task<long> GetCountAsync(string filter = null, Guid? templateId = null);
Task<List<GeneratedReport>> GetByTemplateIdAsync(Guid? templateId);
Task<GeneratedReport> GetByIdWithTemplateAsync(Guid id);
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kurs.Platform.Entities;
using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Repositories
{
public interface IReportTemplateRepository : IRepository<ReportTemplate, Guid>
{
Task<List<ReportTemplate>> GetListAsync(
int skipCount = 0,
int maxResultCount = int.MaxValue,
string sorting = null,
string filter = null,
string category = null
);
Task<long> GetCountAsync(string filter = null, string category = null);
Task<ReportTemplate> GetByIdWithParametersAsync(Guid id);
Task<List<ReportTemplate>> GetByCategoryAsync(string category);
}
}

View file

@ -81,6 +81,11 @@ public class PlatformDbContext :
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
// Reports Entities
public DbSet<ReportTemplate> ReportTemplates { get; set; }
public DbSet<ReportParameter> ReportParameters { get; set; }
public DbSet<GeneratedReport> GeneratedReports { get; set; }
public DbSet<ListFormImport> ListFormImports { get; set; }
public DbSet<ListFormImportExecute> ListFormImportExecutes { get; set; }
@ -685,5 +690,56 @@ public class PlatformDbContext :
b.Property(x => x.Status).IsRequired().HasMaxLength(64);
b.Property(x => x.ErrorsJson).HasColumnType("text");
});
// Reports Configuration
builder.Entity<ReportTemplate>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + nameof(ReportTemplate), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
b.Property(x => x.Description).HasMaxLength(1000);
b.Property(x => x.HtmlContent).IsRequired();
b.Property(x => x.Category).HasMaxLength(100);
b.Property(x => x.Tags).HasMaxLength(2000);
b.HasMany(t => t.Parameters) // navigation üzerinden
.WithOne(p => p.ReportTemplate) // karşı navigation
.HasForeignKey(p => p.ReportTemplateId) // scalar FK property
.OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<ReportParameter>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + nameof(ReportParameter), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.ReportTemplateId).IsRequired();
b.Property(x => x.Name).IsRequired().HasMaxLength(100);
b.Property(x => x.Placeholder).HasMaxLength(200);
b.Property(x => x.Type).IsRequired();
b.Property(x => x.DefaultValue).HasMaxLength(500);
b.Property(x => x.Required).IsRequired();
b.Property(x => x.Description).HasMaxLength(1000);
});
builder.Entity<GeneratedReport>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + nameof(GeneratedReport), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.TemplateId).IsRequired(false); // Nullable yapıyoruz
b.Property(x => x.TemplateName).IsRequired().HasMaxLength(256);
b.Property(x => x.GeneratedContent).IsRequired();
b.Property(x => x.Parameters).HasMaxLength(4000); // JSON string
b.Property(x => x.GeneratedAt).IsRequired();
// Foreign key relationship
b.HasOne(x => x.Template)
.WithMany()
.HasForeignKey(x => x.TemplateId)
.OnDelete(DeleteBehavior.SetNull)
.IsRequired(false);
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,125 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Kurs.Platform.Migrations
{
/// <inheritdoc />
public partial class Reports : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PReportTemplate",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
HtmlContent = table.Column<string>(type: "nvarchar(max)", nullable: false),
Category = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Tags = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, 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_PReportTemplate", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PGeneratedReport",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TemplateId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TemplateName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
GeneratedContent = table.Column<string>(type: "nvarchar(max)", nullable: false),
Parameters = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: true),
GeneratedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, 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_PGeneratedReport", x => x.Id);
table.ForeignKey(
name: "FK_PGeneratedReport_PReportTemplate_TemplateId",
column: x => x.TemplateId,
principalTable: "PReportTemplate",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "PReportParameter",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ReportTemplateId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Placeholder = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Type = table.Column<int>(type: "int", nullable: false),
DefaultValue = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
Required = table.Column<bool>(type: "bit", nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
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_PReportParameter", x => x.Id);
table.ForeignKey(
name: "FK_PReportParameter_PReportTemplate_ReportTemplateId",
column: x => x.ReportTemplateId,
principalTable: "PReportTemplate",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_PGeneratedReport_TemplateId",
table: "PGeneratedReport",
column: "TemplateId");
migrationBuilder.CreateIndex(
name: "IX_PReportParameter_ReportTemplateId",
table: "PReportParameter",
column: "ReportTemplateId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PGeneratedReport");
migrationBuilder.DropTable(
name: "PReportParameter");
migrationBuilder.DropTable(
name: "PReportTemplate");
}
}
}

View file

@ -2036,6 +2036,79 @@ namespace Kurs.Platform.Migrations
b.ToTable("PDataSource", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.GeneratedReport", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
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>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<DateTime>("GeneratedAt")
.HasColumnType("datetime2");
b.Property<string>("GeneratedContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Parameters")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<Guid?>("TemplateId")
.HasColumnType("uniqueidentifier");
b.Property<string>("TemplateName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("TemplateId");
b.ToTable("PGeneratedReport", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.GlobalSearch", b =>
{
b.Property<int>("Id")
@ -2615,7 +2688,6 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.ListFormImportExecute", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("BlobName")
@ -2884,6 +2956,147 @@ namespace Kurs.Platform.Migrations
b.ToTable("PProduct", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", 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(500)
.HasColumnType("nvarchar(500)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Placeholder")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("ReportTemplateId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("Required")
.HasColumnType("bit");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ReportTemplateId");
b.ToTable("PReportParameter", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportTemplate", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
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(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("HtmlContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Tags")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.ToTable("PReportTemplate", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.Route", b =>
{
b.Property<Guid>("Id")
@ -5743,6 +5956,16 @@ namespace Kurs.Platform.Migrations
b.Navigation("Entity");
});
modelBuilder.Entity("Kurs.Platform.Entities.GeneratedReport", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "Template")
.WithMany()
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Template");
});
modelBuilder.Entity("Kurs.Platform.Entities.ListFormCustomization", b =>
{
b.HasOne("Kurs.Platform.Entities.ListForm", null)
@ -5763,6 +5986,17 @@ namespace Kurs.Platform.Migrations
.IsRequired();
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")
.WithMany("Parameters")
.HasForeignKey("ReportTemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ReportTemplate");
});
modelBuilder.Entity("Kurs.Platform.Entities.Uom", b =>
{
b.HasOne("Kurs.Platform.Entities.UomCategory", "UomCategory")
@ -5990,6 +6224,11 @@ namespace Kurs.Platform.Migrations
b.Navigation("Fields");
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportTemplate", b =>
{
b.Navigation("Parameters");
});
modelBuilder.Entity("Kurs.Platform.Entities.UomCategory", b =>
{
b.Navigation("Units");

View file

@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.u5bbu0futh"
"revision": "0.fu6htabtdto"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View file

@ -93,28 +93,6 @@ export const Dashboard: React.FC = () => {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
{/* Header */}
<header className="bg-white shadow-sm border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center space-x-3">
<div className="bg-gradient-to-r from-blue-600 to-indigo-600 p-2 rounded-lg">
<BarChart3 className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-gray-900">Raporlama Sistemi</h1>
<p className="text-sm text-gray-500">Dinamik rapor şablonları ve üretimi</p>
</div>
</div>
<Button onClick={handleCreateTemplate} disabled={isLoading}>
<Plus className="h-4 w-4 mr-2" />
Yeni Şablon
</Button>
</div>
</div>
</header>
{isLoading ? (
<div className="flex items-center justify-center min-h-96">
<div className="text-center">

View file

@ -8,7 +8,7 @@ import { GeneratedReportDto, ReportTemplateDto } from '@/proxy/reports/models'
import { useReports } from '@/utils/hooks/useReports'
export const ReportViewer: React.FC = () => {
const { reportId } = useParams<{ reportId: string }>()
const { id } = useParams<{ id: string }>()
const navigate = useNavigate()
const [zoomLevel, setZoomLevel] = useState(100)
const [report, setReport] = useState<GeneratedReportDto | null>(null)
@ -21,7 +21,7 @@ export const ReportViewer: React.FC = () => {
// Asenkron veri yükleme
useEffect(() => {
const loadReportData = async () => {
if (!reportId) {
if (!id) {
setError("Rapor ID'si bulunamadı")
setIsLoading(false)
return
@ -32,7 +32,7 @@ export const ReportViewer: React.FC = () => {
setError(null)
// Raporu yükle
const reportData = await getReportById(reportId)
const reportData = await getReportById(id)
if (!reportData) {
setError('Rapor bulunamadı')
@ -56,7 +56,7 @@ export const ReportViewer: React.FC = () => {
}
loadReportData()
}, [reportId, getReportById, getTemplateById])
}, [id, getReportById, getTemplateById])
// Zoom fonksiyonları
const handleZoomIn = () => {
@ -124,7 +124,7 @@ export const ReportViewer: React.FC = () => {
}
// Error durumu
if (error || !reportId) {
if (error || !id) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
@ -138,7 +138,7 @@ export const ReportViewer: React.FC = () => {
)
}
if (!reportId) {
if (!id) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">

View file

@ -0,0 +1,22 @@
import React from 'react'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
import { Dashboard } from '@/components/reports/Dashboard'
import { Container } from '@/components/shared'
const DashboardPage: React.FC = () => {
const { translate } = useLocalization()
return (
<Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'App.Reports')}
defaultTitle="Kurs Platform"
></Helmet>
<Dashboard />
</Container>
)
}
export default DashboardPage

View file

@ -0,0 +1,22 @@
import { ReportViewer } from '@/components/reports/ReportViewer'
import { Container } from '@/components/shared'
import { useLocalization } from '@/utils/hooks/useLocalization'
import React from 'react'
import { Helmet } from 'react-helmet'
const ReportViewerPage: React.FC = () => {
const { translate } = useLocalization()
return (
<Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'App.Reports')}
defaultTitle="Kurs Platform"
></Helmet>
<ReportViewer />
</Container>
)
}
export default ReportViewerPage