Report Manager kısmı değiştirildi.

This commit is contained in:
Sedat ÖZTÜRK 2025-10-08 14:31:29 +03:00
parent 8633f0a3f8
commit ffd04bb901
28 changed files with 230 additions and 208 deletions

View file

@ -12,7 +12,7 @@ namespace Kurs.Platform.Reports
[Required]
public string HtmlContent { get; set; }
public string CategoryName { get; set; }
public Guid CategoryId { get; set; }
public List<string> Tags { get; set; }
public List<CreateReportParameterDto> Parameters { get; set; }

View file

@ -6,7 +6,7 @@ namespace Kurs.Platform.Reports
public class GetReportTemplatesInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public string CategoryName { get; set; }
public Guid CategoryId { get; set; }
}
public class GetGeneratedReportsInput : PagedAndSortedResultRequestDto

View file

@ -15,9 +15,9 @@ namespace Kurs.Platform.Reports
Task DeleteTemplateAsync(Guid id);
// Generated Report operations
Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input);
Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id);
Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input);
Task<PagedResultDto<ReportGeneratedDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input);
Task<ReportGeneratedDto> GetGeneratedReportAsync(Guid id);
Task<ReportGeneratedDto> GenerateReportAsync(ReportGenerateDto input);
Task DeleteGeneratedReportAsync(Guid id);
// Bulk operations
@ -27,6 +27,6 @@ namespace Kurs.Platform.Reports
public class ReportsDataDto
{
public PagedResultDto<ReportTemplateDto> Templates { get; set; }
public PagedResultDto<GeneratedReportDto> GeneratedReports { get; set; }
public PagedResultDto<ReportGeneratedDto> GeneratedReports { get; set; }
}
}

View file

@ -4,14 +4,14 @@ using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
public class GenerateReportDto
public class ReportGenerateDto
{
[Required]
public Guid TemplateId { get; set; }
public Dictionary<string, string> Parameters { get; set; }
public GenerateReportDto()
public ReportGenerateDto()
{
Parameters = [];
}

View file

@ -5,7 +5,7 @@ using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Reports
{
public class GeneratedReportDto : FullAuditedEntityDto<Guid>
public class ReportGeneratedDto : FullAuditedEntityDto<Guid>
{
public Guid? TemplateId { get; set; }
@ -15,13 +15,12 @@ namespace Kurs.Platform.Reports
[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()
public ReportTemplateDto ReportTemplate { get; set; }
public ReportGeneratedDto()
{
Parameters = [];
GeneratedAt = DateTime.UtcNow;
}
}
}

View file

@ -6,7 +6,7 @@ namespace Kurs.Platform.Reports
public class ReportParameterDto
{
public Guid Id { get; set; }
public Guid ReportTemplateId { get; set; }
public Guid? TemplateId { get; set; }
[Required]
public string Name { get; set; }
public string Placeholder { get; set; }

View file

@ -13,8 +13,9 @@ namespace Kurs.Platform.Reports
[Required]
public string HtmlContent { get; set; }
public string CategoryName { get; set; }
public Guid CategoryId { get; set; }
public List<string> Tags { get; set; }
public List<ReportParameterDto> Parameters { get; set; }
public ReportTemplateDto()

View file

@ -2,8 +2,8 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Kurs.Platform.Reports
{
namespace Kurs.Platform.Reports;
public class UpdateReportTemplateDto
{
[Required]
@ -12,7 +12,7 @@ namespace Kurs.Platform.Reports
[Required]
public string HtmlContent { get; set; }
public string CategoryName { get; set; }
public Guid CategoryId { get; set; }
public List<string> Tags { get; set; }
public List<UpdateReportParameterDto> Parameters { get; set; }
@ -35,4 +35,3 @@ namespace Kurs.Platform.Reports
public bool Required { get; set; }
public string Description { get; set; }
}
}

View file

@ -51,14 +51,13 @@ public class ReportAppService : PlatformAppService, IReportAppService
{
query = query.Where(x =>
x.Name.Contains(input.Filter) ||
x.Description.Contains(input.Filter) ||
x.CategoryName.Contains(input.Filter)
x.Description.Contains(input.Filter)
);
}
if (!string.IsNullOrWhiteSpace(input.CategoryName))
if (input.CategoryId != Guid.Empty)
{
query = query.Where(x => x.CategoryName == input.CategoryName);
query = query.Where(x => x.CategoryId == input.CategoryId);
}
// Toplam kayıt sayısı
@ -105,7 +104,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
input.Name,
input.Description,
input.HtmlContent,
input.CategoryName)
input.CategoryId)
{
Tags = JsonSerializer.Serialize(input.Tags)
};
@ -141,14 +140,14 @@ public class ReportAppService : PlatformAppService, IReportAppService
template.Name = input.Name;
template.Description = input.Description;
template.HtmlContent = input.HtmlContent;
template.CategoryName = input.CategoryName;
template.CategoryId = input.CategoryId;
template.Tags = JsonSerializer.Serialize(input.Tags ?? []);
// Şablonu hemen persist et (audit alanları için de iyi olur)
await _reportTemplateRepository.UpdateAsync(template, autoSave: true);
// 2) Parametrelerde upsert + artıklarını sil
var existingParams = await _reportParameterRepository.GetListAsync(p => p.ReportTemplateId == id);
var existingParams = await _reportParameterRepository.GetListAsync(p => p.TemplateId == id);
var existingById = existingParams.ToDictionary(p => p.Id, p => p);
var inputParams = input.Parameters ?? new List<UpdateReportParameterDto>();
@ -231,13 +230,13 @@ public class ReportAppService : PlatformAppService, IReportAppService
await _reportTemplateRepository.DeleteAsync(id);
}
public async Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input)
public async Task<PagedResultDto<ReportGeneratedDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input)
{
var query = await _generatedReportRepository.GetQueryableAsync();
// Okuma senaryosu: tracking gerekmiyor + Template'ı eager load edelim
query = query.AsNoTracking()
.Include(x => x.Template);
.Include(x => x.ReportTemplate);
// Filtre
if (!string.IsNullOrWhiteSpace(input.Filter))
@ -260,7 +259,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
if (!string.IsNullOrWhiteSpace(input.Sorting))
query = query.OrderBy(input.Sorting); // ör. "generatedAt DESC" veya "templateName"
else
query = query.OrderByDescending(x => x.GeneratedAt);
query = query.OrderByDescending(x => x.CreationTime);
// Sayfalama
var reports = await AsyncExecuter.ToListAsync(
@ -270,17 +269,17 @@ public class ReportAppService : PlatformAppService, IReportAppService
// DTO map
var reportDtos = reports.Select(MapToGeneratedReportDto).ToList();
return new PagedResultDto<GeneratedReportDto>(totalCount, reportDtos);
return new PagedResultDto<ReportGeneratedDto>(totalCount, reportDtos);
}
public async Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id)
public async Task<ReportGeneratedDto> GetGeneratedReportAsync(Guid id)
{
var report = await _generatedReportRepository.GetAsync(id);
return MapToGeneratedReportDto(report);
}
public async Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input)
public async Task<ReportGeneratedDto> GenerateReportAsync(ReportGenerateDto input)
{
var template = await _reportTemplateRepository.GetAsync(input.TemplateId);
if (template == null)
@ -336,7 +335,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
Name = template.Name,
Description = template.Description,
HtmlContent = template.HtmlContent,
CategoryName = template.CategoryName,
CategoryId = template.CategoryId,
CreationTime = template.CreationTime,
LastModificationTime = template.LastModificationTime,
CreatorId = template.CreatorId,
@ -359,7 +358,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
dto.Parameters = template.Parameters?.Select(p => new ReportParameterDto
{
Id = p.Id,
ReportTemplateId = p.ReportTemplateId,
TemplateId = p.TemplateId,
Name = p.Name,
Placeholder = p.Placeholder,
Type = p.Type,
@ -371,15 +370,14 @@ public class ReportAppService : PlatformAppService, IReportAppService
return dto;
}
private GeneratedReportDto MapToGeneratedReportDto(ReportGenerated report)
private ReportGeneratedDto MapToGeneratedReportDto(ReportGenerated report)
{
var dto = new GeneratedReportDto
var dto = new ReportGeneratedDto
{
Id = report.Id,
TemplateId = report.TemplateId,
TemplateName = report.TemplateName,
GeneratedContent = report.GeneratedContent,
GeneratedAt = report.GeneratedAt,
CreationTime = report.CreationTime,
LastModificationTime = report.LastModificationTime,
CreatorId = report.CreatorId,
@ -399,9 +397,9 @@ public class ReportAppService : PlatformAppService, IReportAppService
}
// Template mapping
if (report.Template != null)
if (report.ReportTemplate != null)
{
dto.Template = MapToReportTemplateDto(report.Template);
dto.ReportTemplate = MapToReportTemplateDto(report.ReportTemplate);
}
return dto;

View file

@ -26,23 +26,22 @@ namespace Kurs.Platform.Reports
CreateMap<CreateReportParameterDto, ReportParameter>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore());
.ForMember(dest => dest.TemplateId, opt => opt.Ignore());
CreateMap<UpdateReportParameterDto, ReportParameter>()
.ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore());
.ForMember(dest => dest.TemplateId, opt => opt.Ignore());
CreateMap<ReportGenerated, GeneratedReportDto>()
CreateMap<ReportGenerated, ReportGeneratedDto>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src =>
ConvertParametersFromJson(src.Parameters)))
.ForMember(dest => dest.Template, opt => opt.MapFrom(src => src.Template));
.ForMember(dest => dest.ReportTemplate, opt => opt.MapFrom(src => src.ReportTemplate));
CreateMap<GenerateReportDto, ReportGenerated>()
CreateMap<ReportGenerateDto, ReportGenerated>()
.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());
.ForMember(dest => dest.GeneratedContent, opt => opt.Ignore());
CreateMap<ReportCategory, ReportCategoryDto>();

View file

@ -24348,7 +24348,7 @@ public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependenc
new()
{
Order = 1,
ColCount = 2,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
@ -24641,7 +24641,7 @@ public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependenc
new EditingFormItemDto { Order = 2, DataField = "BranchId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=showClearButton },
new EditingFormItemDto { Order = 3, DataField = "ClassTypeId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=showClearButton },
new EditingFormItemDto { Order = 4, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 5, DataField = "LevelType", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 5, DataField = "LevelType", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 6, DataField = "LessonCount", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 7, DataField = "LessonDuration", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 8, DataField = "MonthlyPaymentRate", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox },
@ -24805,6 +24805,19 @@ public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependenc
Visible = true,
IsActive = true,
IsDeleted = false,
ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[]
{
new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) }
}),
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.StaticData,
DisplayExpr = "name",
ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
new () { Key="Seviye",Name="Seviye" },
new () { Key="Sınav Eğitimi",Name="Sınav Eğitimi" },
}),
}),
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.Definitions.Level + ".Create",
@ -25014,7 +25027,7 @@ public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependenc
new()
{
Order = 1,
ColCount = 2,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =

View file

@ -2639,6 +2639,12 @@
"en": "Category",
"tr": "Kategori"
},
{
"resourceName": "Platform",
"key": "App.Reports.TemplateEditor.Placeholder.SelectCategory",
"en": "Select Category",
"tr": "Kategori Seç"
},
{
"resourceName": "Platform",
"key": "App.Reports.TemplateEditor.Label.Tags",

View file

@ -12,18 +12,17 @@ namespace Kurs.Platform.Entities
public string TemplateName { get; set; }
public string GeneratedContent { get; set; }
public string Parameters { get; set; }
public DateTime GeneratedAt { get; set; }
public virtual ReportTemplate Template { get; set; }
public virtual ReportTemplate ReportTemplate { get; set; }
public ReportGenerated()
{
GeneratedAt = DateTime.UtcNow;
}
public ReportGenerated(
Guid id,
Guid? templateId,
Guid templateId,
string templateName,
string generatedContent,
string parameters
@ -33,7 +32,6 @@ namespace Kurs.Platform.Entities
TemplateName = templateName;
GeneratedContent = generatedContent;
Parameters = parameters;
GeneratedAt = DateTime.UtcNow;
}
}
}

View file

@ -8,7 +8,7 @@ namespace Kurs.Platform.Entities
{
public Guid? TenantId { get; set; }
public Guid ReportTemplateId { get; set; }
public Guid? TemplateId { get; set; }
public string Name { get; set; }
public string Placeholder { get; set; }
public string Type { get; set; }
@ -24,14 +24,14 @@ namespace Kurs.Platform.Entities
public ReportParameter(
Guid id,
Guid reportTemplateId,
Guid templateId,
string name,
string placeholder,
string type,
bool required = false
) : base(id)
{
ReportTemplateId = reportTemplateId;
TemplateId = templateId;
Name = name;
Placeholder = placeholder;
Type = type;

View file

@ -12,15 +12,18 @@ namespace Kurs.Platform.Entities
public string Name { get; set; }
public string Description { get; set; }
public string HtmlContent { get; set; }
public string CategoryName { get; set; }
public Guid CategoryId { get; set; }
public string Tags { get; set; }
public ReportCategory ReportCategory { get; set; }
public ICollection<ReportParameter> Parameters { get; set; }
public ICollection<ReportGenerated> Generated { get; set; }
public ReportTemplate()
{
Parameters = [];
Generated = [];
}
public ReportTemplate(
@ -28,14 +31,15 @@ namespace Kurs.Platform.Entities
string name,
string description,
string htmlContent,
string categoryName = "Genel"
Guid categoryId
) : base(id)
{
Name = name;
Description = description;
HtmlContent = htmlContent;
CategoryName = categoryName;
Parameters = new List<ReportParameter>();
CategoryId = categoryId;
Parameters = [];
Generated = [];
}
}
}

View file

@ -667,13 +667,11 @@ public class PlatformDbContext :
b.Property(x => x.Description).HasMaxLength(4000); // JSON string
b.Property(x => x.Icon).HasMaxLength(64);
b.HasIndex(x => x.Name).IsUnique();
b.HasMany(x => x.ReportTemplates)
.WithOne(x => x.ReportCategory)
.HasForeignKey(x => x.CategoryName)
.HasPrincipalKey(c => c.Name)
.OnDelete(DeleteBehavior.SetNull);
.HasForeignKey(x => x.CategoryId)
.HasPrincipalKey(c => c.Id)
.OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<ReportTemplate>(b =>
@ -684,12 +682,16 @@ public class PlatformDbContext :
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.CategoryName).HasMaxLength(256);
b.Property(x => x.Tags).HasMaxLength(2000);
b.HasMany(t => t.Parameters)
.WithOne(p => p.ReportTemplate)
.HasForeignKey(p => p.ReportTemplateId)
.HasForeignKey(p => p.TemplateId)
.OnDelete(DeleteBehavior.Cascade);
b.HasMany(t => t.Generated)
.WithOne(p => p.ReportTemplate)
.HasForeignKey(p => p.TemplateId)
.OnDelete(DeleteBehavior.Cascade);
});
@ -698,7 +700,7 @@ public class PlatformDbContext :
b.ToTable(Prefix.DbTableDefault + nameof(ReportParameter), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.ReportTemplateId).IsRequired();
b.Property(x => x.TemplateId).IsRequired();
b.Property(x => x.Name).IsRequired().HasMaxLength(100);
b.Property(x => x.Placeholder).HasMaxLength(200);
b.Property(x => x.Type).IsRequired();
@ -716,13 +718,6 @@ public class PlatformDbContext :
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();
b.HasOne(x => x.Template)
.WithMany()
.HasForeignKey(x => x.TemplateId)
.OnDelete(DeleteBehavior.SetNull)
.IsRequired(false);
});
//Administration

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20251008085215_Initial")]
[Migration("20251008104142_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -5183,9 +5183,6 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("PReportCategory", (string)null);
});
@ -5210,9 +5207,6 @@ namespace Kurs.Platform.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<DateTime>("GeneratedAt")
.HasColumnType("datetime2");
b.Property<string>("GeneratedContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
@ -5306,12 +5300,12 @@ namespace Kurs.Platform.Migrations
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("ReportTemplateId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("Required")
.HasColumnType("bit");
b.Property<Guid>("TemplateId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
@ -5322,7 +5316,7 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("ReportTemplateId");
b.HasIndex("TemplateId");
b.ToTable("PReportParameter", (string)null);
});
@ -5332,9 +5326,8 @@ namespace Kurs.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("CategoryName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<Guid>("CategoryId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
@ -5389,7 +5382,7 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("CategoryName");
b.HasIndex("CategoryId");
b.ToTable("PReportTemplate", (string)null);
});
@ -8832,19 +8825,19 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "Template")
.WithMany()
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")
.WithMany("Generated")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.SetNull);
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Template");
b.Navigation("ReportTemplate");
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")
.WithMany("Parameters")
.HasForeignKey("ReportTemplateId")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -8855,9 +8848,9 @@ namespace Kurs.Platform.Migrations
{
b.HasOne("Kurs.Platform.Entities.ReportCategory", "ReportCategory")
.WithMany("ReportTemplates")
.HasForeignKey("CategoryName")
.HasPrincipalKey("Name")
.OnDelete(DeleteBehavior.SetNull);
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ReportCategory");
});
@ -9167,6 +9160,8 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.ReportTemplate", b =>
{
b.Navigation("Generated");
b.Navigation("Parameters");
});

View file

@ -1727,7 +1727,6 @@ namespace Kurs.Platform.Migrations
constraints: table =>
{
table.PrimaryKey("PK_PReportCategory", x => x.Id);
table.UniqueConstraint("AK_PReportCategory_Name", x => x.Name);
});
migrationBuilder.CreateTable(
@ -3052,7 +3051,7 @@ namespace Kurs.Platform.Migrations
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),
CategoryName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
CategoryId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Tags = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
@ -3066,11 +3065,11 @@ namespace Kurs.Platform.Migrations
{
table.PrimaryKey("PK_PReportTemplate", x => x.Id);
table.ForeignKey(
name: "FK_PReportTemplate_PReportCategory_CategoryName",
column: x => x.CategoryName,
name: "FK_PReportTemplate_PReportCategory_CategoryId",
column: x => x.CategoryId,
principalTable: "PReportCategory",
principalColumn: "Name",
onDelete: ReferentialAction.SetNull);
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
@ -3398,7 +3397,6 @@ namespace Kurs.Platform.Migrations
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),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
@ -3415,7 +3413,7 @@ namespace Kurs.Platform.Migrations
column: x => x.TemplateId,
principalTable: "PReportTemplate",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
@ -3424,7 +3422,7 @@ namespace Kurs.Platform.Migrations
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ReportTemplateId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TemplateId = 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<string>(type: "nvarchar(max)", nullable: false),
@ -3443,8 +3441,8 @@ namespace Kurs.Platform.Migrations
{
table.PrimaryKey("PK_PReportParameter", x => x.Id);
table.ForeignKey(
name: "FK_PReportParameter_PReportTemplate_ReportTemplateId",
column: x => x.ReportTemplateId,
name: "FK_PReportParameter_PReportTemplate_TemplateId",
column: x => x.TemplateId,
principalTable: "PReportTemplate",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
@ -3993,26 +3991,20 @@ namespace Kurs.Platform.Migrations
table: "PNotification",
column: "NotificationRuleId");
migrationBuilder.CreateIndex(
name: "IX_PReportCategory_Name",
table: "PReportCategory",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PReportGenerated_TemplateId",
table: "PReportGenerated",
column: "TemplateId");
migrationBuilder.CreateIndex(
name: "IX_PReportParameter_ReportTemplateId",
name: "IX_PReportParameter_TemplateId",
table: "PReportParameter",
column: "ReportTemplateId");
column: "TemplateId");
migrationBuilder.CreateIndex(
name: "IX_PReportTemplate_CategoryName",
name: "IX_PReportTemplate_CategoryId",
table: "PReportTemplate",
column: "CategoryName");
column: "CategoryId");
migrationBuilder.CreateIndex(
name: "IX_PRoute_Key",

View file

@ -5180,9 +5180,6 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("PReportCategory", (string)null);
});
@ -5207,9 +5204,6 @@ namespace Kurs.Platform.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<DateTime>("GeneratedAt")
.HasColumnType("datetime2");
b.Property<string>("GeneratedContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
@ -5303,12 +5297,12 @@ namespace Kurs.Platform.Migrations
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<Guid>("ReportTemplateId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("Required")
.HasColumnType("bit");
b.Property<Guid>("TemplateId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
@ -5319,7 +5313,7 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("ReportTemplateId");
b.HasIndex("TemplateId");
b.ToTable("PReportParameter", (string)null);
});
@ -5329,9 +5323,8 @@ namespace Kurs.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("CategoryName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<Guid>("CategoryId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
@ -5386,7 +5379,7 @@ namespace Kurs.Platform.Migrations
b.HasKey("Id");
b.HasIndex("CategoryName");
b.HasIndex("CategoryId");
b.ToTable("PReportTemplate", (string)null);
});
@ -8829,19 +8822,19 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "Template")
.WithMany()
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")
.WithMany("Generated")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.SetNull);
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Template");
b.Navigation("ReportTemplate");
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")
.WithMany("Parameters")
.HasForeignKey("ReportTemplateId")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -8852,9 +8845,9 @@ namespace Kurs.Platform.Migrations
{
b.HasOne("Kurs.Platform.Entities.ReportCategory", "ReportCategory")
.WithMany("ReportTemplates")
.HasForeignKey("CategoryName")
.HasPrincipalKey("Name")
.OnDelete(DeleteBehavior.SetNull);
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ReportCategory");
});
@ -9164,6 +9157,8 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.ReportTemplate", b =>
{
b.Navigation("Generated");
b.Navigation("Parameters");
});

View file

@ -57,7 +57,7 @@ export const Dashboard: React.FC = () => {
// Filter by category (if not "tumu-category")
if (selectedCategory && selectedCategory.id !== 'tumu-category') {
filtered = filtered.filter((template) => template.categoryName === selectedCategory.name)
filtered = filtered.filter((template) => template.categoryId === selectedCategory.id)
}
// Filter by search query
@ -167,7 +167,7 @@ export const Dashboard: React.FC = () => {
</div>
{category.id !== 'tumu-category' && (
<span className="text-sm text-gray-500 ml-2">
({templates.filter((t) => t.categoryName === category.name).length})
({templates.filter((t) => t.categoryId === category.id).length})
</span>
)}
{category.id === 'tumu-category' && (
@ -283,6 +283,7 @@ export const Dashboard: React.FC = () => {
<TemplateCard
key={template.id}
template={template}
categories={categories}
onEdit={handleEditTemplate}
onDelete={handleDeleteTemplate}
onGenerate={handleGenerateReport}
@ -313,6 +314,7 @@ export const Dashboard: React.FC = () => {
setGeneratingTemplate(null)
}}
template={generatingTemplate}
categories={categories}
onGenerate={handleReportGeneration}
/>
</>

View file

@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { Button, Input, Dialog } from '../ui'
import { FaFileAlt } from 'react-icons/fa'
import { ReportTemplateDto } from '@/proxy/reports/models'
import { ReportCategoryDto, ReportTemplateDto } from '@/proxy/reports/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { ROUTES_ENUM } from '@/routes/route.constant'
@ -9,6 +9,7 @@ interface ReportGeneratorProps {
isOpen: boolean
onClose: () => void
template: ReportTemplateDto | null
categories: ReportCategoryDto[]
onGenerate: (templateId: string, parameters: Record<string, string>) => Promise<string | null> // Rapor ID'si döndürmek için (async)
}
@ -16,6 +17,7 @@ export const ReportGenerator: React.FC<ReportGeneratorProps> = ({
isOpen,
onClose,
template,
categories,
onGenerate,
}) => {
const [parameterValues, setParameterValues] = useState<Record<string, string>>({})
@ -80,7 +82,7 @@ export const ReportGenerator: React.FC<ReportGeneratorProps> = ({
<p className="text-gray-600 text-sm">{template.description}</p>
<div className="flex items-center mt-2 space-x-2">
<span className="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
{template.categoryName}
{categories.find((c) => c.id === template.categoryId)?.name}
</span>
{template.tags.map((tag) => (
<span key={tag} className="text-xs bg-gray-100 text-gray-800 px-2 py-1 rounded">

View file

@ -23,7 +23,7 @@ export const ReportViewer: React.FC = () => {
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const { translate } = useLocalization()
const { getReportById, getTemplateById } = useReports()
const { getReportById, getTemplateById, categories } = useReports()
// İçeriği sayfalara bölen fonksiyon
const splitContentIntoPages = (content: string) => {
@ -254,7 +254,7 @@ export const ReportViewer: React.FC = () => {
}
pdf.save(
`${report!.templateName}_${new Date(report!.generatedAt).toLocaleDateString('tr-TR')}.pdf`,
`${report!.templateName}_${new Date(report!.creationTime).toLocaleDateString('tr-TR')}.pdf`,
)
} catch (error) {
console.error('PDF oluşturma hatası:', error)
@ -274,7 +274,7 @@ export const ReportViewer: React.FC = () => {
<div className="flex items-center space-x-4 text-sm text-gray-500">
<span className="flex items-center">
<FaCalendarAlt className="h-4 w-4 mr-1" />
{new Date(report.generatedAt).toLocaleDateString('tr-TR', {
{new Date(report.creationTime).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'long',
day: 'numeric',
@ -285,7 +285,7 @@ export const ReportViewer: React.FC = () => {
{template && (
<span className="flex items-center">
<FaFileAlt className="h-4 w-4 mr-1" />
{template.categoryName}
{categories.find((c) => c.id === template.categoryId)?.name}
</span>
)}
</div>

View file

@ -1,11 +1,12 @@
import React from 'react'
import { Button } from '../ui/Button'
import { FaFileAlt, FaEdit, FaTrash, FaPlay } from 'react-icons/fa';
import { ReportTemplateDto } from '@/proxy/reports/models'
import { useLocalization } from '@/utils/hooks/useLocalization';
import { FaFileAlt, FaEdit, FaTrash, FaPlay } from 'react-icons/fa'
import { ReportCategoryDto, ReportTemplateDto } from '@/proxy/reports/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface TemplateCardProps {
template: ReportTemplateDto
categories: ReportCategoryDto[]
onEdit: (template: ReportTemplateDto) => void
onDelete: (id: string) => void
onGenerate: (template: ReportTemplateDto) => void
@ -13,6 +14,7 @@ interface TemplateCardProps {
export const TemplateCard: React.FC<TemplateCardProps> = ({
template,
categories,
onEdit,
onDelete,
onGenerate,
@ -31,14 +33,16 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
<div className="flex items-center gap-1 flex-shrink-0 bg-gray-50 px-2 py-1 rounded-lg">
<FaFileAlt className="h-4 w-4 text-blue-500" />
<span className="text-xs text-gray-500 whitespace-nowrap">{template.parameters.length}</span>
<span className="text-xs text-gray-500 whitespace-nowrap">
{template.parameters.length}
</span>
</div>
</div>
{/* Tags section with proper wrapping */}
<div className="flex items-center gap-1 mb-4 flex-wrap min-h-0">
<span className="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full font-medium flex-shrink-0">
{template.categoryName}
{categories.find((c) => c.id === template.categoryId)?.name}
</span>
{template.tags.slice(0, 2).map((tag) => (
<span
@ -56,13 +60,18 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
{/* Footer with date and actions */}
<div className="flex flex-col gap-3 mt-auto pt-3 border-t border-gray-100">
<div className="text-xs text-gray-500">
<p className="truncate">{new Date(template.lastModificationTime || template.creationTime).toLocaleString('tr-TR', {
<p className="truncate">
{new Date(template.lastModificationTime || template.creationTime).toLocaleString(
'tr-TR',
{
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}</p>
minute: '2-digit',
},
)}
</p>
</div>
<div className="flex items-center justify-center gap-2 w-full">

View file

@ -25,7 +25,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
name: '',
description: '',
htmlContent: '',
categoryName: 'Genel Raporlar',
categoryId: '',
tags: [] as string[],
parameters: [] as ReportParameterDto[],
})
@ -34,13 +34,23 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
const [tagInput, setTagInput] = useState('')
const [isSaving, setIsSaving] = useState(false)
// Debug kategorileri
useEffect(() => {
console.log('Categories received:', categories)
}, [categories])
// Debug form data değişimini takip et
useEffect(() => {
console.log('FormData changed:', formData)
}, [formData])
useEffect(() => {
if (template) {
setFormData({
name: template.name,
description: template.description || '',
htmlContent: template.htmlContent,
categoryName: template.categoryName!,
categoryId: template.categoryId!,
tags: template.tags,
parameters: template.parameters,
})
@ -49,7 +59,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
name: '',
description: '',
htmlContent: '',
categoryName: 'Genel Raporlar',
categoryId: '',
tags: [],
parameters: [],
})
@ -74,7 +84,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
// Yeni parametre oluştur
return {
id: crypto.randomUUID(),
reportTemplateId: template?.id || '',
templateId: template?.id || '',
name: paramName,
placeholder: `@@${paramName}`,
type: 'text',
@ -99,6 +109,9 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
return
}
console.log('FormData before save:', formData)
console.log('Categories available:', categories)
setIsSaving(true)
try {
await onSave(formData as unknown as ReportTemplateDto)
@ -213,17 +226,21 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
{translate('::App.Reports.TemplateEditor.Label.Category')}
</label>
<select
value={formData.categoryName}
onChange={(e) =>
value={formData.categoryId}
onChange={(e) => {
console.log('Category selected:', e.target.value)
setFormData((prev) => ({
...prev,
categoryName: e.target.value,
categoryId: e.target.value,
}))
}
}}
className="block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">
{translate('::App.Reports.TemplateEditor.Placeholder.SelectCategory')}
</option>
{categories.map((category) => (
<option key={category.id} value={category.name}>
<option key={category.id} value={category.id}>
{category.name}
</option>
))}

View file

@ -9,7 +9,7 @@ export interface ReportCategoryDto {
export interface ReportParameterDto {
id: string
reportTemplateId: string
templateId: string
name: string
placeholder?: string
type: ReportParameterType
@ -23,7 +23,7 @@ export interface ReportTemplateDto {
name: string
description?: string
htmlContent: string
categoryName?: string
categoryId?: string
tags: string[]
parameters: ReportParameterDto[]
@ -40,7 +40,6 @@ export interface ReportGeneratedDto {
templateName: string
generatedContent: string
parameters: Record<string, string>
generatedAt: string // ISO
// FullAuditedEntityDto alanları
creationTime: string // ISO
@ -69,7 +68,7 @@ export interface CreateReportTemplateDto {
name: string
description?: string
htmlContent: string
categoryName?: string
categoryId?: string
tags?: string[]
parameters: CreateReportParameterDto[]
}
@ -78,7 +77,7 @@ export interface UpdateReportTemplateDto {
name: string
description?: string
htmlContent: string
categoryName?: string
categoryId?: string
tags?: string[]
parameters: UpdateReportParameterDto[]
}
@ -95,7 +94,7 @@ export interface GetReportTemplatesInput {
maxResultCount?: number
sorting?: string
filter?: string
categoryName?: string
categoryId?: string
}
export interface GetReportsGeneratedInput {

View file

@ -38,7 +38,7 @@ export class ReportsService {
skipCount: input.skipCount,
maxResultCount: input.maxResultCount,
filter: input.filter,
categoryName: input.categoryName,
categoryId: input.categoryId,
},
},
{ apiName: this.apiName },

View file

@ -191,14 +191,14 @@ export const useReports = () => {
[data.templates],
)
const loadTemplatesByCategory = useCallback(async (categoryName?: string) => {
const loadTemplatesByCategory = useCallback(async (categoryId?: string) => {
setIsLoading(true)
try {
const templatesResponse = await reportsService.getTemplates({
sorting: '',
skipCount: 0,
maxResultCount: 1000,
categoryName: categoryName && categoryName !== 'Tümü' ? categoryName : undefined,
categoryId: categoryId && categoryId !== 'Tümü' ? categoryId : undefined,
})
setData((prevData) => ({

View file

@ -1,4 +1,4 @@
import { FaFolder, FaCommentDots, FaFileAlt, FaChartLine } from 'react-icons/fa';
import { FaFolder, FaCommentDots, FaFileAlt, FaChartLine } from 'react-icons/fa'
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
import { useLocalization } from '@/utils/hooks/useLocalization'
import dayjs from 'dayjs'
@ -62,7 +62,6 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
dayjs.extend(relativeTime)
dayjs.locale('tr')
// Topics -> "New topic created in {categoryName}"
topics.forEach((topic) => {
const category = categories.find((c) => c.id === topic.categoryId)
if (topic.creationTime) {