Report App Service2

This commit is contained in:
Sedat ÖZTÜRK 2025-08-15 14:52:30 +03:00
parent cd51347f3b
commit 8d3f39a2e9
17 changed files with 217 additions and 377 deletions

View file

@ -16,12 +16,12 @@ namespace Kurs.Platform.Reports;
public class ReportAppService : PlatformAppService, IReportAppService public class ReportAppService : PlatformAppService, IReportAppService
{ {
private readonly IRepository<ReportTemplate, Guid> _reportTemplateRepository; private readonly IRepository<ReportTemplate, Guid> _reportTemplateRepository;
private readonly IRepository<GeneratedReport, Guid> _generatedReportRepository; private readonly IRepository<ReportGenerated, Guid> _generatedReportRepository;
private readonly IRepository<ReportParameter, Guid> _reportParameterRepository; private readonly IRepository<ReportParameter, Guid> _reportParameterRepository;
public ReportAppService( public ReportAppService(
IRepository<ReportTemplate, Guid> reportTemplateRepository, IRepository<ReportTemplate, Guid> reportTemplateRepository,
IRepository<GeneratedReport, Guid> generatedReportRepository, IRepository<ReportGenerated, Guid> generatedReportRepository,
IRepository<ReportParameter, Guid> reportParameterRepository) IRepository<ReportParameter, Guid> reportParameterRepository)
{ {
_reportTemplateRepository = reportTemplateRepository; _reportTemplateRepository = reportTemplateRepository;
@ -222,7 +222,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
generatedContent = generatedContent.Replace(pattern, param.Value ?? ""); generatedContent = generatedContent.Replace(pattern, param.Value ?? "");
} }
var generatedReport = new GeneratedReport( var generatedReport = new ReportGenerated(
GuidGenerator.Create(), GuidGenerator.Create(),
template.Id, template.Id,
template.Name, template.Name,
@ -297,7 +297,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
return dto; return dto;
} }
private GeneratedReportDto MapToGeneratedReportDto(GeneratedReport report) private GeneratedReportDto MapToGeneratedReportDto(ReportGenerated report)
{ {
var dto = new GeneratedReportDto var dto = new GeneratedReportDto
{ {

View file

@ -34,12 +34,12 @@ namespace Kurs.Platform.Reports
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => (Entities.ReportParameterType)src.Type)) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => (Entities.ReportParameterType)src.Type))
.ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore()); .ForMember(dest => dest.ReportTemplateId, opt => opt.Ignore());
CreateMap<GeneratedReport, GeneratedReportDto>() CreateMap<ReportGenerated, GeneratedReportDto>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => .ForMember(dest => dest.Parameters, opt => opt.MapFrom(src =>
ConvertParametersFromJson(src.Parameters))) ConvertParametersFromJson(src.Parameters)))
.ForMember(dest => dest.Template, opt => opt.MapFrom(src => src.Template)); .ForMember(dest => dest.Template, opt => opt.MapFrom(src => src.Template));
CreateMap<GenerateReportDto, GeneratedReport>() CreateMap<GenerateReportDto, ReportGenerated>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => ConvertParametersToJson(src.Parameters))) .ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => ConvertParametersToJson(src.Parameters)))
.ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.TemplateId, opt => opt.MapFrom(src => src.TemplateId)) .ForMember(dest => dest.TemplateId, opt => opt.MapFrom(src => src.TemplateId))

View file

@ -25660,7 +25660,7 @@
}, },
{ {
"key": "admin.reports.view", "key": "admin.reports.view",
"path": "/admin/reports/:id", "path": "/admin/report/:id",
"componentPath": "@/views/report/ReportViewerPage", "componentPath": "@/views/report/ReportViewerPage",
"routeType": "protected", "routeType": "protected",
"authority": ["App.Reports.Update"] "authority": ["App.Reports.Update"]

View file

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities.Auditing;
namespace Kurs.Platform.Entities namespace Kurs.Platform.Entities
{ {
public class GeneratedReport : FullAuditedAggregateRoot<Guid> public class ReportGenerated : FullAuditedAggregateRoot<Guid>
{ {
public Guid? TemplateId { get; set; } public Guid? TemplateId { get; set; }
@ -22,12 +22,12 @@ namespace Kurs.Platform.Entities
// Navigation property // Navigation property
public virtual ReportTemplate Template { get; set; } public virtual ReportTemplate Template { get; set; }
public GeneratedReport() public ReportGenerated()
{ {
GeneratedAt = DateTime.UtcNow; GeneratedAt = DateTime.UtcNow;
} }
public GeneratedReport( public ReportGenerated(
Guid id, Guid id,
Guid? templateId, Guid? templateId,
string templateName, string templateName,

View file

@ -6,9 +6,9 @@ using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Repositories namespace Kurs.Platform.Repositories
{ {
public interface IGeneratedReportRepository : IRepository<GeneratedReport, Guid> public interface IGeneratedReportRepository : IRepository<ReportGenerated, Guid>
{ {
Task<List<GeneratedReport>> GetListAsync( Task<List<ReportGenerated>> GetListAsync(
int skipCount = 0, int skipCount = 0,
int maxResultCount = int.MaxValue, int maxResultCount = int.MaxValue,
string sorting = null, string sorting = null,
@ -18,8 +18,8 @@ namespace Kurs.Platform.Repositories
Task<long> GetCountAsync(string filter = null, Guid? templateId = null); Task<long> GetCountAsync(string filter = null, Guid? templateId = null);
Task<List<GeneratedReport>> GetByTemplateIdAsync(Guid? templateId); Task<List<ReportGenerated>> GetByTemplateIdAsync(Guid? templateId);
Task<GeneratedReport> GetByIdWithTemplateAsync(Guid id); Task<ReportGenerated> GetByIdWithTemplateAsync(Guid id);
} }
} }

View file

@ -84,7 +84,7 @@ public class PlatformDbContext :
// Reports Entities // Reports Entities
public DbSet<ReportTemplate> ReportTemplates { get; set; } public DbSet<ReportTemplate> ReportTemplates { get; set; }
public DbSet<ReportParameter> ReportParameters { get; set; } public DbSet<ReportParameter> ReportParameters { get; set; }
public DbSet<GeneratedReport> GeneratedReports { get; set; } public DbSet<ReportGenerated> GeneratedReports { get; set; }
public DbSet<ListFormImport> ListFormImports { get; set; } public DbSet<ListFormImport> ListFormImports { get; set; }
public DbSet<ListFormImportExecute> ListFormImportExecutes { get; set; } public DbSet<ListFormImportExecute> ListFormImportExecutes { get; set; }
@ -723,9 +723,9 @@ public class PlatformDbContext :
b.Property(x => x.Description).HasMaxLength(1000); b.Property(x => x.Description).HasMaxLength(1000);
}); });
builder.Entity<GeneratedReport>(b => builder.Entity<ReportGenerated>(b =>
{ {
b.ToTable(PlatformConsts.DbTablePrefix + nameof(GeneratedReport), PlatformConsts.DbSchema); b.ToTable(PlatformConsts.DbTablePrefix + nameof(ReportGenerated), PlatformConsts.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.TemplateId).IsRequired(false); // Nullable yapıyoruz b.Property(x => x.TemplateId).IsRequired(false); // Nullable yapıyoruz

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20250815110914_Reports")] [Migration("20250815115111_Reports")]
partial class Reports partial class Reports
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -2039,79 +2039,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PDataSource", (string)null); 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 => modelBuilder.Entity("Kurs.Platform.Entities.GlobalSearch", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -2959,6 +2886,79 @@ namespace Kurs.Platform.Migrations
b.ToTable("PProduct", (string)null); b.ToTable("PProduct", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", 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("PReportGenerated", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b => modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -5959,16 +5959,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Entity"); 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 => modelBuilder.Entity("Kurs.Platform.Entities.ListFormCustomization", b =>
{ {
b.HasOne("Kurs.Platform.Entities.ListForm", null) b.HasOne("Kurs.Platform.Entities.ListForm", null)
@ -5989,6 +5979,16 @@ namespace Kurs.Platform.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "Template")
.WithMany()
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Template");
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b => modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{ {
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate") b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")

View file

@ -37,7 +37,7 @@ namespace Kurs.Platform.Migrations
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "PGeneratedReport", name: "PReportGenerated",
columns: table => new columns: table => new
{ {
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
@ -58,9 +58,9 @@ namespace Kurs.Platform.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_PGeneratedReport", x => x.Id); table.PrimaryKey("PK_PReportGenerated", x => x.Id);
table.ForeignKey( table.ForeignKey(
name: "FK_PGeneratedReport_PReportTemplate_TemplateId", name: "FK_PReportGenerated_PReportTemplate_TemplateId",
column: x => x.TemplateId, column: x => x.TemplateId,
principalTable: "PReportTemplate", principalTable: "PReportTemplate",
principalColumn: "Id", principalColumn: "Id",
@ -99,8 +99,8 @@ namespace Kurs.Platform.Migrations
}); });
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PGeneratedReport_TemplateId", name: "IX_PReportGenerated_TemplateId",
table: "PGeneratedReport", table: "PReportGenerated",
column: "TemplateId"); column: "TemplateId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
@ -113,7 +113,7 @@ namespace Kurs.Platform.Migrations
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PGeneratedReport"); name: "PReportGenerated");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PReportParameter"); name: "PReportParameter");

View file

@ -2036,79 +2036,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PDataSource", (string)null); 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 => modelBuilder.Entity("Kurs.Platform.Entities.GlobalSearch", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -2956,6 +2883,79 @@ namespace Kurs.Platform.Migrations
b.ToTable("PProduct", (string)null); b.ToTable("PProduct", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", 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("PReportGenerated", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b => modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -5956,16 +5956,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Entity"); 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 => modelBuilder.Entity("Kurs.Platform.Entities.ListFormCustomization", b =>
{ {
b.HasOne("Kurs.Platform.Entities.ListForm", null) b.HasOne("Kurs.Platform.Entities.ListForm", null)
@ -5986,6 +5976,16 @@ namespace Kurs.Platform.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ReportGenerated", b =>
{
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "Template")
.WithMany()
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Template");
});
modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b => modelBuilder.Entity("Kurs.Platform.Entities.ReportParameter", b =>
{ {
b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate") b.HasOne("Kurs.Platform.Entities.ReportTemplate", "ReportTemplate")

View file

@ -28,7 +28,7 @@ export const Dashboard: React.FC = () => {
return templates.filter((template) => { return templates.filter((template) => {
const matchesSearch = const matchesSearch =
template.name.toLowerCase().includes(searchQuery.toLowerCase()) || template.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
template.description.toLowerCase().includes(searchQuery.toLowerCase()) || template.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
template.tags.some((tag: any) => tag.toLowerCase().includes(searchQuery.toLowerCase())) template.tags.some((tag: any) => tag.toLowerCase().includes(searchQuery.toLowerCase()))
const matchesCategory = selectedCategory === 'Tümü' || template.category === selectedCategory const matchesCategory = selectedCategory === 'Tümü' || template.category === selectedCategory

View file

@ -1,140 +0,0 @@
import React from 'react'
import { Plus, Trash2 } from 'lucide-react'
import { ReportParameterDto } from '@/proxy/reports/models'
import { Button, Input } from '../ui'
interface ParameterFormProps {
parameters: ReportParameterDto[]
onChange: (parameters: ReportParameterDto[]) => void
}
export const ParameterForm: React.FC<ParameterFormProps> = ({ parameters, onChange }) => {
const addParameter = () => {
const newParameter: ReportParameterDto = {
id: crypto.randomUUID(),
name: '',
placeholder: '',
type: 'text',
required: false,
description: '',
}
onChange([...parameters, newParameter])
}
const updateParameter = (id: string, updates: Partial<ReportParameterDto>) => {
onChange(parameters.map((param) => (param.id === id ? { ...param, ...updates } : param)))
}
const removeParameter = (id: string) => {
onChange(parameters.filter((param) => param.id !== id))
}
const generatePlaceholder = (name: string) => {
return `@@${name.toUpperCase()}`
}
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium text-gray-900">Parametre Tanımları</h3>
<Button onClick={addParameter} size="sm">
<Plus className="h-4 w-4 mr-2" />
Parametre Ekle
</Button>
</div>
{parameters.length === 0 ? (
<div className="py-8 text-gray-500">
<p>Henüz parametre eklenmemiş.</p>
<p className="text-sm">Dinamik içerik için parametreler ekleyin.</p>
</div>
) : (
<div className="space-y-4">
{parameters.map((param, index) => (
<div key={param.id} className="bg-gray-50 p-4 rounded-lg border border-gray-200">
<div className="flex items-start justify-between mb-4">
<h4 className="font-medium text-gray-900">Parametre #{index + 1}</h4>
<Button variant="solid" size="sm" onClick={() => removeParameter(param.id)}>
<Trash2 className="h-4 w-4" />
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Input
value={param.name}
onChange={(e) => {
const name = e.target.value
updateParameter(param.id, {
name,
placeholder: generatePlaceholder(name),
})
}}
placeholder="Parametre Adı Örn: SIRKETADI"
/>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Parametre Tipi
</label>
<select
value={param.type}
onChange={(e) =>
updateParameter(param.id, {
type: e.target.value as ReportParameterDto['type'],
})
}
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="text">Metin</option>
<option value="number">Sayı</option>
<option value="date">Tarih</option>
<option value="email">E-posta</option>
<option value="url">URL</option>
</select>
</div>
<Input
placeholder="Varsayılan Değer"
value={param.defaultValue || ''}
onChange={(e) => updateParameter(param.id, { defaultValue: e.target.value })}
/>
<div className="flex items-center">
<input
type="checkbox"
id={`required-${param.id}`}
checked={param.required}
onChange={(e) => updateParameter(param.id, { required: e.target.checked })}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label htmlFor={`required-${param.id}`} className="ml-2 text-sm text-gray-700">
Zorunlu parametre
</label>
</div>
</div>
<div className="mt-4">
<Input
value={param.description || ''}
onChange={(e) => updateParameter(param.id, { description: e.target.value })}
placeholder="Açıklama"
rows={2}
textArea={true}
/>
</div>
{param.name && (
<div className="mt-3 p-2 bg-blue-50 rounded border-l-4 border-blue-400">
<p className="text-sm text-blue-700">
<strong>Kullanım:</strong>{' '}
<code className="bg-blue-100 px-1 rounded">{param.placeholder}</code>
</p>
</div>
)}
</div>
))}
</div>
)}
</div>
)
}

View file

@ -4,14 +4,14 @@ import { Button } from '../ui/Button'
import { ArrowLeft, Calendar, FileText, Download, ZoomIn, ZoomOut } from 'lucide-react' import { ArrowLeft, Calendar, FileText, Download, ZoomIn, ZoomOut } from 'lucide-react'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import jsPDF from 'jspdf' import jsPDF from 'jspdf'
import { GeneratedReportDto, ReportTemplateDto } from '@/proxy/reports/models' import { ReportGeneratedDto, ReportTemplateDto } from '@/proxy/reports/models'
import { useReports } from '@/utils/hooks/useReports' import { useReports } from '@/utils/hooks/useReports'
export const ReportViewer: React.FC = () => { export const ReportViewer: React.FC = () => {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const navigate = useNavigate() const navigate = useNavigate()
const [zoomLevel, setZoomLevel] = useState(100) const [zoomLevel, setZoomLevel] = useState(100)
const [report, setReport] = useState<GeneratedReportDto | null>(null) const [report, setReport] = useState<ReportGeneratedDto | null>(null)
const [template, setTemplate] = useState<ReportTemplateDto | null>(null) const [template, setTemplate] = useState<ReportTemplateDto | null>(null)
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)

View file

@ -16,10 +16,6 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
onDelete, onDelete,
onGenerate, onGenerate,
}) => { }) => {
const formatDate = (date: Date) => {
return new Date(date).toLocaleDateString('tr-TR')
}
return ( return (
<div className="bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-200 border border-gray-200"> <div className="bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-200 border border-gray-200">
<div className="p-6"> <div className="p-6">
@ -54,7 +50,7 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
<p>Güncellenme: {formatDate(template.lastModificationTime)}</p> <p>Güncellenme: {template.lastModificationTime}</p>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">

View file

@ -34,9 +34,9 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
if (template) { if (template) {
setFormData({ setFormData({
name: template.name, name: template.name,
description: template.description, description: template.description || '',
htmlContent: template.htmlContent, htmlContent: template.htmlContent,
category: template.category, category: template.category || 'Genel',
tags: template.tags, tags: template.tags,
parameters: template.parameters, parameters: template.parameters,
}) })
@ -70,6 +70,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
// Yeni parametre oluştur // Yeni parametre oluştur
return { return {
id: crypto.randomUUID(), id: crypto.randomUUID(),
reportTemplateId: template?.id || '',
name: paramName, name: paramName,
placeholder: `@@${paramName}`, placeholder: `@@${paramName}`,
type: 'text', type: 'text',
@ -96,7 +97,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
setIsSaving(true) setIsSaving(true)
try { try {
await onSave(formData) await onSave(formData as unknown as ReportTemplateDto)
onClose() onClose()
} catch (error) { } catch (error) {
console.error('Error saving template:', error) console.error('Error saving template:', error)
@ -166,6 +167,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-full"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-full">
<div className="space-y-4"> <div className="space-y-4">
<Input <Input
autoFocus
value={formData.name} value={formData.name}
onChange={(e) => onChange={(e) =>
setFormData((prev) => ({ setFormData((prev) => ({

View file

@ -1,23 +1,11 @@
// reports.models.ts export type ReportParameterType = 'text' | 'number' | 'date' | 'select' | 'checkbox'
/** Enum, backend ile birebir (0..4) */
export enum ReportParameterType {
Text = 0,
Number = 1,
Date = 2,
Email = 3,
Url = 4,
}
/** ---- API MODELLERİ (Raw JSON) ---- */
/** Not: Tarihler APIden ISO string olarak gelir (Date değil) */
export interface ReportParameterDto { export interface ReportParameterDto {
id: string id: string
reportTemplateId: string reportTemplateId: string
name: string name: string
placeholder?: string placeholder?: string
type: ReportParameterType // enum (0..4) type: ReportParameterType
defaultValue?: string defaultValue?: string
required: boolean required: boolean
description?: string description?: string
@ -39,7 +27,7 @@ export interface ReportTemplateDto {
lastModifierId?: string lastModifierId?: string
} }
export interface GeneratedReportDto { export interface ReportGeneratedDto {
id: string id: string
templateId?: string | null templateId?: string | null
templateName: string templateName: string
@ -89,7 +77,7 @@ export interface UpdateReportTemplateDto {
} }
/** Generate inputu */ /** Generate inputu */
export interface GenerateReportDto { export interface ReportGenerateDto {
templateId: string templateId: string
parameters: Record<string, string> parameters: Record<string, string>
} }
@ -103,16 +91,10 @@ export interface GetReportTemplatesInput {
category?: string category?: string
} }
export interface GetGeneratedReportsInput { export interface GetReportsGeneratedInput {
skipCount?: number skipCount?: number
maxResultCount?: number maxResultCount?: number
sorting?: string sorting?: string
filter?: string filter?: string
templateId?: string templateId?: string
} }
/** (Opsiyonel) Paged wrapper — projende zaten varsa bunu kullanmana gerek yok */
export interface PagedResultDto<T> {
items: T[]
totalCount: number
}

View file

@ -1,16 +1,16 @@
import { import {
ReportTemplateDto, ReportTemplateDto,
GeneratedReportDto, ReportGeneratedDto,
CreateReportTemplateDto, CreateReportTemplateDto,
UpdateReportTemplateDto, UpdateReportTemplateDto,
GenerateReportDto, // backend'deki GenerateReportDto (templateId + parameters) ReportGenerateDto, // backend'deki GenerateReportDto (templateId + parameters)
} from '@/proxy/reports/models' } from '@/proxy/reports/models'
import apiService from './api.service' import apiService from './api.service'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy' import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
export interface ReportsData { export interface ReportsData {
templates: ReportTemplateDto[] templates: ReportTemplateDto[]
generatedReports: GeneratedReportDto[] generatedReports: ReportGeneratedDto[]
} }
export class ReportsService { export class ReportsService {
@ -71,7 +71,7 @@ export class ReportsService {
// GENERATED REPORTS // GENERATED REPORTS
getGeneratedReports = (input: PagedAndSortedResultRequestDto) => getGeneratedReports = (input: PagedAndSortedResultRequestDto) =>
apiService.fetchData<PagedResultDto<GeneratedReportDto>, PagedAndSortedResultRequestDto>( apiService.fetchData<PagedResultDto<ReportGeneratedDto>, PagedAndSortedResultRequestDto>(
{ {
method: 'GET', method: 'GET',
url: '/api/app/report/generated-reports', // ✔ Swagger: GET /api/app/report/generated-reports url: '/api/app/report/generated-reports', // ✔ Swagger: GET /api/app/report/generated-reports
@ -85,7 +85,7 @@ export class ReportsService {
) )
getGeneratedReportById = (id: string) => getGeneratedReportById = (id: string) =>
apiService.fetchData<GeneratedReportDto>( apiService.fetchData<ReportGeneratedDto>(
{ {
method: 'GET', method: 'GET',
url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: GET /api/app/report/{id}/generated-report url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: GET /api/app/report/{id}/generated-report
@ -93,8 +93,8 @@ export class ReportsService {
{ apiName: this.apiName }, { apiName: this.apiName },
) )
generateReport = (input: GenerateReportDto) => generateReport = (input: ReportGenerateDto) =>
apiService.fetchData<GeneratedReportDto, GenerateReportDto>( apiService.fetchData<ReportGeneratedDto, ReportGenerateDto>(
{ {
method: 'POST', method: 'POST',
url: '/api/app/report/generate-report', // ✔ Swagger: POST /api/app/report/generate-report url: '/api/app/report/generate-report', // ✔ Swagger: POST /api/app/report/generate-report

View file

@ -1,4 +1,4 @@
import { GeneratedReportDto, ReportTemplateDto } from '@/proxy/reports/models' import { ReportGeneratedDto, ReportTemplateDto } from '@/proxy/reports/models'
import ReportsService from '@/services/reports.service' import ReportsService from '@/services/reports.service'
import { useState, useCallback, useEffect } from 'react' import { useState, useCallback, useEffect } from 'react'
@ -6,7 +6,7 @@ const reportsService = new ReportsService()
interface ReportData { interface ReportData {
templates: ReportTemplateDto[] templates: ReportTemplateDto[]
generatedReports: GeneratedReportDto[] generatedReports: ReportGeneratedDto[]
} }
export const useReports = () => { export const useReports = () => {
@ -127,13 +127,13 @@ export const useReports = () => {
async (templateId: string, parameterValues: Record<string, string>) => { async (templateId: string, parameterValues: Record<string, string>) => {
setIsLoading(true) setIsLoading(true)
try { try {
const reportData: GeneratedReportDto = { const reportData: ReportGeneratedDto = {
templateId, templateId,
parameters: parameterValues, parameters: parameterValues,
} as GeneratedReportDto } as ReportGeneratedDto
const response = await reportsService.generateReport(reportData) const response = await reportsService.generateReport(reportData)
const report = response.data as GeneratedReportDto const report = response.data as ReportGeneratedDto
if (report) { if (report) {
// Update local state // Update local state
@ -158,7 +158,7 @@ export const useReports = () => {
async (reportId: string) => { async (reportId: string) => {
try { try {
const response = await reportsService.getGeneratedReportById(reportId) const response = await reportsService.getGeneratedReportById(reportId)
return response.data as GeneratedReportDto return response.data as ReportGeneratedDto
} catch (error) { } catch (error) {
console.error('Error getting report by id:', error) console.error('Error getting report by id:', error)
// Fallback to local data // Fallback to local data