Devexpress Grid Raporu otomatik oluşturma

This commit is contained in:
Sedat ÖZTÜRK 2026-06-16 12:04:55 +03:00
parent 3cff822775
commit 6884812484
7 changed files with 594 additions and 38 deletions

View file

@ -1586,6 +1586,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
AuthName = listFormName, AuthName = listFormName,
Url=$"/admin/reports/TemplateReport/view?id=@Id", Url=$"/admin/reports/TemplateReport/view?id=@Id",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
}, },
new() { new() {
Hint = "App.Platform.Design", Hint = "App.Platform.Design",
@ -1594,6 +1595,16 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
AuthName = listFormName + ".Update", AuthName = listFormName + ".Update",
Url=$"/admin/reports/TemplateReport/design?id=@Id", Url=$"/admin/reports/TemplateReport/design?id=@Id",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
},
new() {
Hint = "App.Platform.View",
Text ="App.Platform.View",
UrlTarget="_blank",
AuthName = listFormName,
Url=$"/admin/reports/DynamicListFormReport/view?listFormCode=@Name",
IsVisible = true,
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
}, },
}), }),
} }

View file

@ -1373,8 +1373,8 @@
"icon": "✍️" "icon": "✍️"
}, },
{ {
"name": "Sözleşmeler", "name": "Listeler",
"description": "Tedarikçiler ile ilgili sözleşme raporları", "description": "Tüm listelerin raporları",
"icon": "📜" "icon": "📜"
} }
], ],
@ -1384,7 +1384,14 @@
"description": "Standard Taahhütname", "description": "Standard Taahhütname",
"categoryName": "Taahhütnameler", "categoryName": "Taahhütnameler",
"htmlContent": "<h1>Taahhütname</h1><p>Bu belge, tarafların belirli yükümlülüklerini ve taahhütlerini belirtir.</p>", "htmlContent": "<h1>Taahhütname</h1><p>Bu belge, tarafların belirli yükümlülüklerini ve taahhütlerini belirtir.</p>",
"status": "Aktif" "status": "active"
},
{
"name": "App.Definitions.AiBot",
"description": "Liste Raporları",
"categoryName": "Listeler",
"htmlContent": "<p>Liste Raporları.</p>",
"status": "active"
} }
], ],
"WorkHours": [ "WorkHours": [

View file

@ -1,14 +1,16 @@
using DevExpress.AspNetCore.Reporting.WebDocumentViewer; using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services; using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Sozsoft.Platform.Controllers; namespace Sozsoft.Platform.Controllers;
[Route("DXXRDV")] [Route("DXXRDV")]
[Authorize]
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
public class CustomWebDocumentViewerController : WebDocumentViewerController public class CustomWebDocumentViewerController : WebDocumentViewerController
{ {
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService)
: base(controllerService) : base(controllerService)
{ {
} }

View file

@ -0,0 +1,480 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using DevExpress.DataAccess.ConnectionParameters;
using DevExpress.DataAccess.Sql;
using DevExpress.Drawing;
using DevExpress.XtraPrinting;
using DevExpress.XtraReports.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Primitives;
using Sozsoft.Platform.DynamicData;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Enums;
using Sozsoft.Platform.ListForms;
using Sozsoft.Platform.Localization;
using Sozsoft.Platform.Queries;
namespace Sozsoft.Reports.PredefinedReports;
public class DynamicListFormReport : XtraReport
{
public const string ReportName = nameof(DynamicListFormReport);
private const string QueryName = "DynamicList";
private const int A4PortraitWidth = 827;
private const int HorizontalMargin = 35;
private const int LandscapeColumnThreshold = 5;
private static IServiceProvider _serviceProvider;
public static void SetServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public static DynamicListFormReport Create(IDictionary<string, string> parameters)
{
var report = new DynamicListFormReport();
report.Build(parameters);
return report;
}
public DynamicListFormReport()
{
}
private void Build(IDictionary<string, string> parameters)
{
if (_serviceProvider == null)
{
throw new InvalidOperationException("DynamicListFormReport service provider is not configured.");
}
if (!parameters.TryGetValue("listFormCode", out var listFormCode) || string.IsNullOrWhiteSpace(listFormCode))
{
throw new ArgumentException("listFormCode parameter is required.");
}
using var scope = _serviceProvider.CreateScope();
var authManager = scope.ServiceProvider.GetRequiredService<IListFormAuthorizationManager>();
var listFormManager = scope.ServiceProvider.GetRequiredService<IListFormManager>();
var listFormFieldManager = scope.ServiceProvider.GetRequiredService<IListFormFieldManager>();
var customizationManager = scope.ServiceProvider.GetRequiredService<IListFormCustomizationManager>();
var dynamicDataManager = scope.ServiceProvider.GetRequiredService<IDynamicDataManager>();
var selectQueryManager = scope.ServiceProvider.GetRequiredService<ISelectQueryManager>();
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
if (!authManager.CanAccess(listFormCode).GetAwaiter().GetResult())
{
throw new UnauthorizedAccessException($"No read permission for list form '{listFormCode}'.");
}
var listForm = listFormManager.GetUserListForm(listFormCode).GetAwaiter().GetResult();
var fields = listFormFieldManager.GetUserListFormFields(listFormCode).GetAwaiter().GetResult()
.Where(x => x.CanRead)
.OrderBy(x => x.ListOrderNo ?? int.MaxValue)
.ThenBy(x => x.CaptionName)
.ToList();
var customizations = customizationManager.GetUsersServerCustomizations(listFormCode).GetAwaiter().GetResult();
var (_, connectionString, dataSourceType) = dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode).GetAwaiter().GetResult();
var queryParameters = ToStringValues(parameters);
var queryParams = new QueryParameters
{
ListFormCode = listFormCode,
Filter = GetParameter(parameters, "filter"),
Sort = GetParameter(parameters, "sort"),
Group = GetParameter(parameters, "group"),
GroupSummary = GetParameter(parameters, "groupSummary"),
TotalSummary = GetParameter(parameters, "totalSummary"),
Take = 0,
Skip = 0
};
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
var visibleFields = fields
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
.Where(x => x.IsActive != false && x.Visible == true)
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
.ToList();
if (visibleFields.Count == 0)
{
visibleFields = fields
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
.Where(x => x.IsActive != false)
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
.Take(8)
.ToList();
}
var reportColumns = visibleFields
.Select(field =>
{
var selectField = selectQueryManager.SelectFields.First(s => s.FieldName == field.FieldName);
return new ReportColumn(field, GetReportFieldName(selectField));
})
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
.ToList();
ConfigurePage(reportColumns);
ConfigureDataSource(
ApplyQueryParameters(selectQueryManager.SelectQuery, selectQueryManager.SelectQueryParameters),
connectionString,
dataSourceType);
BuildLayout(listForm, reportColumns, localizer);
RequestParameters = false;
}
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields)
{
Margins = new System.Drawing.Printing.Margins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
var requestedContentWidth = visibleFields.Sum(x => Math.Max(x.Field.Width ?? 90, 60));
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
Font = new DXFont("Arial", 8.25F);
}
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
{
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
dataSource.Queries.Add(new CustomSqlQuery(QueryName, selectQuery));
ComponentStorage.Add(dataSource);
DataSource = dataSource;
DataMember = QueryName;
}
private static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
{
if (string.IsNullOrWhiteSpace(connectionString) ||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
{
return connectionString;
}
var provider = dataSourceType switch
{
DataSourceTypeEnum.Mssql => "MSSqlServer",
DataSourceTypeEnum.Postgresql => "Postgres",
_ => null
};
return string.IsNullOrWhiteSpace(provider)
? connectionString
: $"XpoProvider={provider};{connectionString}";
}
private static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
{
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
{
return sql;
}
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
{
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
sql = Regex.Replace(
sql,
Regex.Escape(key) + @"\b",
ToSqlLiteral(parameter.Value),
RegexOptions.CultureInvariant);
}
return sql;
}
private static string ToSqlLiteral(object value)
{
if (value == null || value == DBNull.Value)
{
return "NULL";
}
return value switch
{
string text => $"'{text.Replace("'", "''")}'",
Guid guid => $"'{guid}'",
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
bool boolean => boolean ? "1" : "0",
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
=> Convert.ToString(value, CultureInfo.InvariantCulture),
_ => $"'{value.ToString()?.Replace("'", "''")}'"
};
}
private void BuildLayout(
ListForm listForm,
IReadOnlyList<ReportColumn> fields,
IStringLocalizer<PlatformResource> localizer)
{
Bands.Clear();
var topMargin = new TopMarginBand { HeightF = Margins.Top };
var bottomMargin = new BottomMarginBand { HeightF = 32 };
var reportHeader = new ReportHeaderBand { HeightF = 42 };
var pageHeader = new PageHeaderBand { HeightF = 28 };
var detail = new DetailBand { HeightF = 24 };
var reportFooter = new ReportFooterBand { HeightF = HasAnySummary(fields) ? 28 : 0 };
Bands.AddRange([topMargin, reportHeader, pageHeader, detail, reportFooter, bottomMargin]);
var pageWidth = PageWidth - Margins.Left - Margins.Right;
reportHeader.Controls.Add(new XRLabel
{
Text = GetLocalizedReportTitle(listForm, localizer),
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 30),
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
TextAlignment = TextAlignment.MiddleLeft
});
var generatedAt = new XRPageInfo
{
PageInfo = PageInfo.DateTime,
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
BoundsF = new System.Drawing.RectangleF(0, 30, pageWidth / 2, 12),
Font = new DXFont("Arial", 7.5F),
TextAlignment = TextAlignment.MiddleLeft
};
reportHeader.Controls.Add(generatedAt);
var pageInfo = new XRPageInfo
{
TextFormatString = "Sayfa {0} / {1}",
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
Font = new DXFont("Arial", 8F),
TextAlignment = TextAlignment.MiddleRight
};
bottomMargin.Controls.Add(pageInfo);
var widths = CalculateWidths(fields, pageWidth);
var headerTable = CreateTable(pageWidth, 28);
var headerRow = new XRTableRow();
var detailTable = CreateTable(pageWidth, 24);
var detailRow = new XRTableRow();
var footerTable = CreateTable(pageWidth, 24);
var footerRow = new XRTableRow();
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
headerRow.Cells.Add(CreateHeaderCell(field, widths[i], localizer));
detailRow.Cells.Add(CreateDetailCell(field, widths[i]));
footerRow.Cells.Add(CreateFooterCell(field, widths[i]));
}
headerTable.Rows.Add(headerRow);
detailTable.Rows.Add(detailRow);
footerTable.Rows.Add(footerRow);
pageHeader.Controls.Add(headerTable);
detail.Controls.Add(detailTable);
if (HasAnySummary(fields))
{
reportFooter.Controls.Add(footerTable);
}
}
private static XRTable CreateTable(float width, float height)
{
return new XRTable
{
BoundsF = new System.Drawing.RectangleF(0, 0, width, height),
Borders = BorderSide.All,
BorderWidth = 0.5F
};
}
private static XRTableCell CreateHeaderCell(
ReportColumn column,
float width,
IStringLocalizer<PlatformResource> localizer)
{
var field = column.Field;
return new XRTableCell
{
Text = GetLocalizedFieldCaption(field, localizer),
WidthF = width,
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
Padding = new PaddingInfo(4, 4, 2, 2),
TextAlignment = TextAlignment.MiddleCenter,
Multiline = true
};
}
private static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
{
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
return Localize(localizer, title, listForm.ListFormCode);
}
private static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
{
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
return Localize(localizer, caption, field.FieldName);
}
private static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
{
if (string.IsNullOrWhiteSpace(key))
{
return fallback;
}
var localized = localizer[key];
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
? key
: localized.Value;
}
private static XRTableCell CreateDetailCell(ReportColumn column, float width)
{
var field = column.Field;
var cell = new XRTableCell
{
WidthF = width,
Font = new DXFont("Arial", 8F),
Padding = new PaddingInfo(4, 4, 2, 2),
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType),
Multiline = false,
CanGrow = false
};
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{column.BindingName}]"));
return cell;
}
private static XRTableCell CreateFooterCell(ReportColumn column, float width)
{
var field = column.Field;
var summaryType = GetSummaryType(field.TotalSummaryJson);
var cell = new XRTableCell
{
WidthF = width,
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
Padding = new PaddingInfo(4, 4, 2, 2),
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType)
};
if (summaryType == null)
{
return cell;
}
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"sumSum([{column.BindingName}])"));
cell.Summary = new XRSummary
{
Running = SummaryRunning.Report,
Func = summaryType.Value,
IgnoreNullValues = true
};
return cell;
}
private static float[] CalculateWidths(IReadOnlyList<ListFormField> fields, float pageWidth)
{
var raw = fields.Select(x => (float)Math.Max(x.Width ?? 90, 60)).ToArray();
var total = raw.Sum();
if (total <= 0)
{
return fields.Select(_ => pageWidth / fields.Count).ToArray();
}
return raw.Select(x => pageWidth * x / total).ToArray();
}
private static float[] CalculateWidths(IReadOnlyList<ReportColumn> fields, float pageWidth)
{
var raw = fields.Select(x => (float)Math.Max(x.Field.Width ?? 90, 60)).ToArray();
var total = raw.Sum();
if (total <= 0)
{
return fields.Select(_ => pageWidth / fields.Count).ToArray();
}
return raw.Select(x => pageWidth * x / total).ToArray();
}
private static bool HasAnySummary(IEnumerable<ReportColumn> fields)
{
return fields.Any(x => GetSummaryType(x.Field.TotalSummaryJson) != null);
}
private static string GetReportFieldName(SelectField selectField)
{
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
{
return selectField.JoinOptions.FieldNameAlias;
}
return selectField?.FieldName;
}
private sealed record ReportColumn(ListFormField Field, string BindingName);
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
{
if (string.IsNullOrWhiteSpace(totalSummaryJson))
{
return null;
}
var value = totalSummaryJson.ToLowerInvariant();
if (value.Contains("\"summarytype\":\"sum\""))
{
return SummaryFunc.Sum;
}
if (value.Contains("\"summarytype\":\"avg\""))
{
return SummaryFunc.Avg;
}
if (value.Contains("\"summarytype\":\"min\""))
{
return SummaryFunc.Min;
}
if (value.Contains("\"summarytype\":\"max\""))
{
return SummaryFunc.Max;
}
if (value.Contains("\"summarytype\":\"count\""))
{
return SummaryFunc.Count;
}
return null;
}
private static TextAlignment ToTextAlignment(string alignment, DbType dbType)
{
return alignment?.ToLowerInvariant() switch
{
"center" => TextAlignment.MiddleCenter,
"right" => TextAlignment.MiddleRight,
"left" => TextAlignment.MiddleLeft,
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
};
}
private static bool IsNumeric(DbType dbType)
{
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
}
private static string GetParameter(IDictionary<string, string> parameters, string name)
{
return parameters.TryGetValue(name, out var value) ? value : null;
}
private static Dictionary<string, StringValues> ToStringValues(IDictionary<string, string> parameters)
{
return parameters.ToDictionary(x => x.Key, x => new StringValues(x.Value));
}
}

View file

@ -21,6 +21,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
// TemplateReport için ServiceProvider'ı set et // TemplateReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider); Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
if (!Directory.Exists(reportDirectory)) if (!Directory.Exists(reportDirectory))
{ {
@ -34,6 +35,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
// TemplateReport için ServiceProvider'ı set et // TemplateReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider); Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
if (!Directory.Exists(reportDirectory)) if (!Directory.Exists(reportDirectory))
{ {
@ -65,8 +67,8 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
// Implement your own logic to prohibit URLs that contain spaces or other specific characters. // Implement your own logic to prohibit URLs that contain spaces or other specific characters.
// Return **true** if no validation is required. // Return **true** if no validation is required.
// Query string varsa sadece rapor adını kontrol et // Query string encoded gelse bile sadece rapor adını kontrol et.
string reportName = url.Contains("?") ? url.Split('?')[0] : url; var (reportName, _) = ParseReportUrl(url);
return Path.GetFileName(reportName) == reportName; return Path.GetFileName(reportName) == reportName;
} }
@ -78,26 +80,14 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
// if the parameters are included in the report URL's query string. // if the parameters are included in the report URL's query string.
try try
{ {
// URL'den rapor adını ve parametreleri ayır var (reportName, parameters) = ParseReportUrl(url);
string reportName = url;
Dictionary<string, string> parameters = new Dictionary<string, string>();
if (url.Contains("?")) if (reportName == Sozsoft.Reports.PredefinedReports.DynamicListFormReport.ReportName)
{ {
var parts = url.Split('?'); var report = Sozsoft.Reports.PredefinedReports.DynamicListFormReport.Create(parameters);
reportName = parts[0]; using MemoryStream ms = new MemoryStream();
report.SaveLayoutToXml(ms);
// Query string parametrelerini parse et return ms.ToArray();
var queryString = parts[1];
var paramPairs = queryString.Split('&');
foreach (var pair in paramPairs)
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{
parameters[keyValue[0]] = Uri.UnescapeDataString(keyValue[1]);
}
}
} }
// Önce Reports klasöründen yüklemeyi dene // Önce Reports klasöründen yüklemeyi dene
@ -177,6 +167,51 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData"); throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData");
} }
private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url)
{
var decodedUrl = DecodeRepeatedly(url);
var reportName = decodedUrl;
var parameters = new Dictionary<string, string>();
var queryIndex = decodedUrl.IndexOf('?');
if (queryIndex < 0)
{
return (reportName, parameters);
}
reportName = decodedUrl[..queryIndex];
var queryString = decodedUrl[(queryIndex + 1)..];
var paramPairs = queryString.Split('&', StringSplitOptions.RemoveEmptyEntries);
foreach (var pair in paramPairs)
{
var keyValue = pair.Split('=', 2);
if (keyValue.Length == 2 && !string.IsNullOrWhiteSpace(keyValue[0]))
{
parameters[keyValue[0]] = keyValue[1];
}
}
return (reportName, parameters);
}
private static string DecodeRepeatedly(string value)
{
var decoded = value;
for (var i = 0; i < 3; i++)
{
var next = Uri.UnescapeDataString(decoded);
if (next == decoded)
{
return decoded;
}
decoded = next;
}
return decoded;
}
public override Dictionary<string, string> GetUrls() public override Dictionary<string, string> GetUrls()
{ {
// Returns a dictionary that contains the report names (URLs) and display names. // Returns a dictionary that contains the report names (URLs) and display names.

View file

@ -50,8 +50,8 @@ export const ROUTES_ENUM = {
dynamicServicesEdit: '/admin/developerkit/dynamic-services/edit/:id', dynamicServicesEdit: '/admin/developerkit/dynamic-services/edit/:id',
}, },
reports: { reports: {
generator: '/admin/reports/generator', view: '/admin/reports/:id/view',
view: '/admin/reports/:id', design: '/admin/reports/:id/design',
}, },
}, },
admin: { admin: {

View file

@ -1,15 +1,14 @@
import React, { useMemo, lazy, Suspense, useEffect } from 'react' import React, { useMemo, useEffect, useState } from 'react'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { useParams, useLocation } from 'react-router-dom' import { useParams, useLocation } from 'react-router-dom'
import { RequestOptions } from 'devexpress-reporting-react/dx-report-viewer' import ReportViewer, {
RequestOptions,
} from 'devexpress-reporting-react/dx-report-viewer'
import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native'
import { APP_NAME } from '@/constants/app.constant' import { APP_NAME } from '@/constants/app.constant'
import { useStoreState } from '@/store'
// Lazy load
const ReportViewer = lazy(() =>
import('devexpress-reporting-react/dx-report-viewer')
)
const loadViewerCss = () => { const loadViewerCss = () => {
const styles = [ const styles = [
@ -31,11 +30,33 @@ const DevexpressReportViewer: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const location = useLocation() const location = useLocation()
const token = useStoreState((state) => state.auth.session.token)
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
useEffect(() => { useEffect(() => {
loadViewerCss() loadViewerCss()
}, []) }, [])
useEffect(() => {
setIsRequestAuthReady(false)
const authorization = token ? `Bearer ${token}` : undefined
const ajaxHeaders = ajaxSetup.ajaxSettings.headers ?? {}
if (authorization) {
ajaxSetup.ajaxSettings.headers = {
...ajaxHeaders,
Authorization: authorization,
}
setIsRequestAuthReady(true)
return
}
const { Authorization: _ajaxAuthorization, ...nextAjaxHeaders } = ajaxHeaders
ajaxSetup.ajaxSettings.headers = nextAjaxHeaders
setIsRequestAuthReady(true)
}, [token])
const reportUrlWithParams = useMemo(() => { const reportUrlWithParams = useMemo(() => {
if (!id) return '' if (!id) return ''
return location.search ? `${id}${location.search}` : id return location.search ? `${id}${location.search}` : id
@ -51,14 +72,14 @@ const DevexpressReportViewer: React.FC = () => {
defaultTitle={APP_NAME} defaultTitle={APP_NAME}
/> />
<Suspense fallback={<div>Rapor yükleniyor...</div>}> {isRequestAuthReady && (
<ReportViewer reportUrl={reportUrlWithParams}> <ReportViewer reportUrl={reportUrlWithParams}>
<RequestOptions <RequestOptions
host={`${import.meta.env.VITE_API_URL}/`} host={import.meta.env.VITE_API_URL}
invokeAction="DXXRDV" invokeAction="/DXXRDV"
/> />
</ReportViewer> </ReportViewer>
</Suspense> )}
</Container> </Container>
) )
} }