DynamicForm Url değişiklikleri ve Rapor Aç

This commit is contained in:
Sedat Öztürk 2026-06-17 00:19:14 +03:00
parent fdf48c04ee
commit 1997a8aacb
13 changed files with 313 additions and 93 deletions

View file

@ -1398,6 +1398,18 @@
"en": "Menu Management", "en": "Menu Management",
"tr": "Menü Yönetimi" "tr": "Menü Yönetimi"
}, },
{
"resourceName": "Platform",
"key": "App.Reports.List",
"en": "List Report",
"tr": "Liste Raporu"
},
{
"resourceName": "Platform",
"key": "App.Reports.Form",
"en": "Form Report",
"tr": "Form Raporu"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Reports", "key": "App.Reports",

View file

@ -1584,7 +1584,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Text ="App.Platform.View", Text ="App.Platform.View",
UrlTarget="_blank", UrlTarget="_blank",
AuthName = listFormName, AuthName = listFormName,
Url=$"/admin/reports/DynamicReport/view?id=@Id", Url=$"/admin/reports/DynamicReport/view/@Id",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')" VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
}, },
@ -1593,7 +1593,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Text ="App.Platform.Design", Text ="App.Platform.Design",
UrlTarget="_blank", UrlTarget="_blank",
AuthName = listFormName + ".Update", AuthName = listFormName + ".Update",
Url=$"/admin/reports/DynamicReport/design?id=@Id", Url=$"/admin/reports/DynamicReport/design/@Id",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')" VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
}, },
@ -1602,7 +1602,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Text ="App.Platform.View", Text ="App.Platform.View",
UrlTarget="_blank", UrlTarget="_blank",
AuthName = listFormName, AuthName = listFormName,
Url=$"/admin/reports/DynamicGrid/view?listFormCode=@Name", Url=$"/admin/reports/DynamicGrid/view/@Name",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Listeler')" VisibleExpression = "((e)=> e.row.data.CategoryId === 'Listeler')"
}, },
@ -1611,7 +1611,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Text ="App.Platform.View", Text ="App.Platform.View",
UrlTarget="_blank", UrlTarget="_blank",
AuthName = listFormName, AuthName = listFormName,
Url=$"/admin/reports/DynamicGrid/view?listFormCode=@Name&id=@Id", Url=$"/admin/reports/DynamicGrid/view/@Id/@Name",
IsVisible = true, IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Formlar')" VisibleExpression = "((e)=> e.row.data.CategoryId === 'Formlar')"
}, },

View file

@ -418,7 +418,7 @@
}, },
{ {
"key": "admin.devexpressDynamicReportView", "key": "admin.devexpressDynamicReportView",
"path": "/admin/reports/:id/view", "path": "/admin/reports/:report/view/:id?/:listFormCode?",
"componentType": "normal", "componentType": "normal",
"componentPath": "@/views/report/DevexpressReportViewer", "componentPath": "@/views/report/DevexpressReportViewer",
"routeType": "protected", "routeType": "protected",
@ -426,7 +426,7 @@
}, },
{ {
"key": "admin.devexpressDynamicReportDesigner", "key": "admin.devexpressDynamicReportDesigner",
"path": "/admin/reports/:id/design", "path": "/admin/reports/:report/design/:id?/:listFormCode?",
"componentType": "normal", "componentType": "normal",
"componentPath": "@/views/report/DevexpressReportDesigner", "componentPath": "@/views/report/DevexpressReportDesigner",
"routeType": "protected", "routeType": "protected",

View file

@ -1383,21 +1383,21 @@
"name": "Standard Taahhütname", "name": "Standard Taahhütname",
"description": "Standard Taahhütname", "description": "Standard Taahhütname",
"categoryName": "Raporlar", "categoryName": "Raporlar",
"htmlContent": "<h1>Taahhütname</h1><p>http://localhost:3000/admin/reports/DynamicReport/view?id=ede12de7-0e94-4b84-a953-6c359606478b</p>", "htmlContent": "<h1>Taahhütname</h1><p>http://localhost:3000/admin/reports/DynamicReport/view/ede12de7-0e94-4b84-a953-6c359606478b</p>",
"status": "active" "status": "active"
}, },
{ {
"name": "App.Languages.LanguageText", "name": "App.Languages.LanguageText",
"description": "Liste Raporları", "description": "Liste Raporları",
"categoryName": "Listeler", "categoryName": "Listeler",
"htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicGridReport/view?listFormCode=App.Languages.LanguageText&filter=[%22CultureName%22,%22=%22,%22en%22]</p>", "htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicGridReport/view/App.Languages.LanguageText?filter=[%22CultureName%22,%22=%22,%22en%22]</p>",
"status": "active" "status": "active"
}, },
{ {
"name": "App.Languages.Language", "name": "App.Languages.Language",
"description": "Form Raporları", "description": "Form Raporları",
"categoryName": "Formlar", "categoryName": "Formlar",
"htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicFormReport/view?listFormCode=App.Languages.Language&id=e2c8a2e0-af22-f7bb-5d88-3a21e3079997</p>", "htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicFormReport/view/e2c8a2e0-af22-f7bb-5d88-3a21e3079997/App.Languages.Language</p>",
"status": "active" "status": "active"
} }
], ],

View file

@ -28,6 +28,7 @@ public class DynamicFormReport : XtraReport
private const string QueryName = "DynamicForm"; private const string QueryName = "DynamicForm";
private const int HorizontalMargin = 35; private const int HorizontalMargin = 35;
private const float RowHeight = 24; private const float RowHeight = 24;
private static readonly System.Drawing.Color FieldBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
private static IServiceProvider _serviceProvider; private static IServiceProvider _serviceProvider;
public static void SetServiceProvider(IServiceProvider serviceProvider) public static void SetServiceProvider(IServiceProvider serviceProvider)
@ -107,6 +108,7 @@ public class DynamicFormReport : XtraReport
reportFields, reportFields,
defaultValueHelper); defaultValueHelper);
selectQuery = ApplyQueryParameters(selectQuery, selectQueryManager.SelectQueryParameters); selectQuery = ApplyQueryParameters(selectQuery, selectQueryManager.SelectQueryParameters);
selectQuery = StripReportPaging(selectQuery);
var row = GetFirstRow(dynamicDataRepository.QueryAsync(selectQuery, connectionString).GetAwaiter().GetResult()); var row = GetFirstRow(dynamicDataRepository.QueryAsync(selectQuery, connectionString).GetAwaiter().GetResult());
@ -118,12 +120,23 @@ public class DynamicFormReport : XtraReport
private void ConfigurePage() private void ConfigurePage()
{ {
Margins = new System.Drawing.Printing.Margins(HorizontalMargin, HorizontalMargin, 35, 35); Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4; PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
Landscape = false; Landscape = false;
Font = new DXFont("Arial", 8.25F); Font = new DXFont("Arial", 8.25F);
} }
#pragma warning disable CA1416
private static System.Drawing.Printing.Margins CreateReportMargins(
int left,
int right,
int top,
int bottom)
{
return new System.Drawing.Printing.Margins(left, right, top, bottom);
}
#pragma warning restore CA1416
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType) private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
{ {
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType))); var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
@ -195,7 +208,7 @@ public class DynamicFormReport : XtraReport
var rowHeight = GetFieldRowHeight(field); var rowHeight = GetFieldRowHeight(field);
var labelWidth = pageWidth * 0.28F; var labelWidth = pageWidth * 0.28F;
detail.Controls.Add(CreateCaptionLabel(field, localizer, 0, y, labelWidth, rowHeight)); detail.Controls.Add(CreateCaptionLabel(field, localizer, 0, y, labelWidth, rowHeight));
detail.Controls.Add(CreateValueLabel(field, labelWidth, y, pageWidth - labelWidth, rowHeight)); detail.Controls.Add(CreateValueLabel(field, row, labelWidth, y, pageWidth - labelWidth, rowHeight));
y += rowHeight + 3; y += rowHeight + 3;
} }
@ -230,48 +243,75 @@ public class DynamicFormReport : XtraReport
Font = new DXFont("Arial", 8F, DXFontStyle.Bold), Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
BackColor = System.Drawing.Color.FromArgb(248, 250, 252), BackColor = System.Drawing.Color.FromArgb(248, 250, 252),
Borders = BorderSide.All, Borders = BorderSide.All,
BorderWidth = 0.5F, BorderColor = FieldBorderColor,
BorderWidth = 0.25F,
Padding = new PaddingInfo(5, 5, 2, 2), Padding = new PaddingInfo(5, 5, 2, 2),
TextAlignment = TextAlignment.MiddleLeft, TextAlignment = TextAlignment.MiddleLeft,
Multiline = true Multiline = true
}; };
} }
private static XRLabel CreateValueLabel(FormReportField field, float x, float y, float width, float height) private static XRLabel CreateValueLabel(
FormReportField field,
IDictionary<string, object> row,
float x,
float y,
float width,
float height)
{ {
var label = new XRLabel var label = new XRLabel
{ {
BoundsF = new System.Drawing.RectangleF(x, y, width, height), BoundsF = new System.Drawing.RectangleF(x, y, width, height),
Font = new DXFont("Arial", 8F), Font = new DXFont("Arial", 8F),
Borders = BorderSide.All, Borders = BorderSide.All,
BorderWidth = 0.5F, BorderColor = FieldBorderColor,
BorderWidth = 0.25F,
Padding = new PaddingInfo(5, 5, 2, 2), Padding = new PaddingInfo(5, 5, 2, 2),
TextAlignment = ToTextAlignment(field.Field.Alignment, field.Field.SourceDbType), TextAlignment = ToTextAlignment(field.Field.Alignment, field.Field.SourceDbType),
Multiline = true, Multiline = true,
CanGrow = true CanGrow = true
}; };
if (TryGetFormattedValue(field, row, out var text))
{
label.Text = text;
}
else
{
label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{field.BindingName}]")); label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{field.BindingName}]"));
if (IsBooleanEditor(field) || field.StaticLookupValues?.Count > 0)
{
label.BeforePrint += (_, _) =>
{
var value = Convert.ToString(label.Text, CultureInfo.InvariantCulture);
if (IsBooleanEditor(field) && TryFormatBoolean(value, out var booleanText))
{
label.Text = booleanText;
}
else if (value != null && field.StaticLookupValues?.TryGetValue(value, out var displayText) == true)
{
label.Text = displayText;
}
};
} }
return label; return label;
} }
private static bool TryGetFormattedValue(
FormReportField field,
IDictionary<string, object> row,
out string text)
{
text = null;
if (row == null || !TryGetRowValue(row, field.BindingName, out var rawValue))
{
return false;
}
var value = Convert.ToString(rawValue, CultureInfo.InvariantCulture);
if (IsBooleanEditor(field) && TryFormatBoolean(value, out var booleanText))
{
text = booleanText;
return true;
}
if (value != null && field.StaticLookupValues?.TryGetValue(value, out var displayText) == true)
{
text = displayText;
return true;
}
text = value ?? string.Empty;
return true;
}
private static float CalculateDetailHeight( private static float CalculateDetailHeight(
IReadOnlyList<FormReportField> fields, IReadOnlyList<FormReportField> fields,
ListForm listForm, ListForm listForm,
@ -546,6 +586,20 @@ public class DynamicFormReport : XtraReport
return sql; return sql;
} }
private static string StripReportPaging(string sql)
{
if (string.IsNullOrWhiteSpace(sql))
{
return sql;
}
return Regex.Replace(
sql,
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
string.Empty,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private static string ToSqlLiteral(object value) private static string ToSqlLiteral(object value)
{ {
if (value == null || value == DBNull.Value) if (value == null || value == DBNull.Value)
@ -741,7 +795,7 @@ public class DynamicFormReport : XtraReport
var query = lookupQuery.Trim().TrimEnd(';'); var query = lookupQuery.Trim().TrimEnd(';');
return Regex.Replace( return Regex.Replace(
query, query,
@"\s+ORDER\s+BY\s+[\s\S]*$", @"(?<![A-Za-z0-9_])ORDER\s+BY\b[\s\S]*$",
string.Empty, string.Empty,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
} }

View file

@ -28,6 +28,7 @@ public class DynamicGridReport : XtraReport
private const int A4PortraitWidth = 827; private const int A4PortraitWidth = 827;
private const int HorizontalMargin = 35; private const int HorizontalMargin = 35;
private const int LandscapeColumnThreshold = 5; private const int LandscapeColumnThreshold = 5;
private static readonly System.Drawing.Color TableBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
private static IServiceProvider _serviceProvider; private static IServiceProvider _serviceProvider;
public static void SetServiceProvider(IServiceProvider serviceProvider) public static void SetServiceProvider(IServiceProvider serviceProvider)
@ -126,19 +127,21 @@ public class DynamicGridReport : XtraReport
listForm, listForm,
reportColumns, reportColumns,
defaultValueHelper); defaultValueHelper);
selectQuery = StripReportPaging(ApplyQueryParameters(selectQuery, selectQueryManager.SelectQueryParameters));
ConfigurePage(reportColumns); ConfigurePage(reportColumns);
ConfigureDataSource( ConfigureDataSource(
ApplyQueryParameters(selectQuery, selectQueryManager.SelectQueryParameters), selectQuery,
connectionString, connectionString,
dataSourceType); dataSourceType);
BuildLayout(listForm, reportColumns, localizer); BuildLayout(listForm, reportColumns, localizer);
RequestParameters = false; RequestParameters = false;
} }
#pragma warning disable CA1416
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields) private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields)
{ {
Margins = new System.Drawing.Printing.Margins(HorizontalMargin, HorizontalMargin, 35, 35); Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4; PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right; var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
var requestedContentWidth = visibleFields.Sum(x => Math.Max(x.Field.Width ?? 90, 60)); var requestedContentWidth = visibleFields.Sum(x => Math.Max(x.Field.Width ?? 90, 60));
@ -146,6 +149,16 @@ public class DynamicGridReport : XtraReport
Font = new DXFont("Arial", 8.25F); Font = new DXFont("Arial", 8.25F);
} }
private static System.Drawing.Printing.Margins CreateReportMargins(
int left,
int right,
int top,
int bottom)
{
return new System.Drawing.Printing.Margins(left, right, top, bottom);
}
#pragma warning restore CA1416
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType) private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
{ {
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType))); var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
@ -195,6 +208,20 @@ public class DynamicGridReport : XtraReport
return sql; return sql;
} }
private static string StripReportPaging(string sql)
{
if (string.IsNullOrWhiteSpace(sql))
{
return sql;
}
return Regex.Replace(
sql,
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
string.Empty,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private static string ToSqlLiteral(object value) private static string ToSqlLiteral(object value)
{ {
if (value == null || value == DBNull.Value) if (value == null || value == DBNull.Value)
@ -293,7 +320,8 @@ public class DynamicGridReport : XtraReport
{ {
BoundsF = new System.Drawing.RectangleF(0, 0, width, height), BoundsF = new System.Drawing.RectangleF(0, 0, width, height),
Borders = BorderSide.All, Borders = BorderSide.All,
BorderWidth = 0.5F BorderColor = TableBorderColor,
BorderWidth = 0.25F
}; };
} }
@ -353,17 +381,6 @@ public class DynamicGridReport : XtraReport
CanGrow = false CanGrow = false
}; };
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{column.BindingName}]")); cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{column.BindingName}]"));
if (column.StaticLookupValues?.Count > 0)
{
cell.BeforePrint += (_, _) =>
{
var value = Convert.ToString(cell.Text, CultureInfo.InvariantCulture);
if (value != null && column.StaticLookupValues.TryGetValue(value, out var displayText))
{
cell.Text = displayText;
}
};
}
return cell; return cell;
} }
@ -455,11 +472,12 @@ public class DynamicGridReport : XtraReport
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData) if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
{ {
var staticLookupValues = GetStaticLookupValues(lookup, defaultValueHelper);
return new ReportColumn( return new ReportColumn(
field, field,
bindingName, staticLookupValues?.Count > 0 ? GetLookupBindingName(field) : bindingName,
lookup, lookup,
GetStaticLookupValues(lookup, defaultValueHelper)); staticLookupValues);
} }
return new ReportColumn(field, bindingName, null, null); return new ReportColumn(field, bindingName, null, null);
@ -570,11 +588,15 @@ public class DynamicGridReport : XtraReport
IReadOnlyList<ReportColumn> reportColumns, IReadOnlyList<ReportColumn> reportColumns,
DefaultValueHelper defaultValueHelper) DefaultValueHelper defaultValueHelper)
{ {
var lookupColumns = reportColumns var queryLookupColumns = reportColumns
.Where(x => x.Lookup != null && IsReportQueryableLookup(x.Lookup)) .Where(x => x.Lookup != null && IsReportQueryableLookup(x.Lookup))
.ToList(); .ToList();
var staticLookupColumns = reportColumns
.Where(x => x.StaticLookupValues?.Count > 0)
.ToList();
if (lookupColumns.Count == 0 || string.IsNullOrWhiteSpace(selectQuery)) if ((queryLookupColumns.Count == 0 && staticLookupColumns.Count == 0) ||
string.IsNullOrWhiteSpace(selectQuery))
{ {
return selectQuery; return selectQuery;
} }
@ -585,7 +607,12 @@ public class DynamicGridReport : XtraReport
var selectParts = new List<string>(); var selectParts = new List<string>();
var joinParts = new List<string>(); var joinParts = new List<string>();
foreach (var column in lookupColumns) foreach (var column in staticLookupColumns)
{
selectParts.Add(CreateStaticLookupSelectPart(column, tableAlias));
}
foreach (var column in queryLookupColumns)
{ {
var lookup = column.Lookup; var lookup = column.Lookup;
var alias = GetLookupAlias(column.Field); var alias = GetLookupAlias(column.Field);
@ -612,6 +639,10 @@ public class DynamicGridReport : XtraReport
{ {
joinInsertIndex = FindClauseIndex(selectQuery, " ORDER BY "); joinInsertIndex = FindClauseIndex(selectQuery, " ORDER BY ");
} }
if (joinParts.Count == 0)
{
return selectQuery;
}
if (joinInsertIndex < 0) if (joinInsertIndex < 0)
{ {
return selectQuery + " " + string.Join(' ', joinParts); return selectQuery + " " + string.Join(' ', joinParts);
@ -620,6 +651,15 @@ public class DynamicGridReport : XtraReport
return selectQuery.Insert(joinInsertIndex, " " + string.Join(' ', joinParts)); return selectQuery.Insert(joinInsertIndex, " " + string.Join(' ', joinParts));
} }
private static string CreateStaticLookupSelectPart(ReportColumn column, string tableAlias)
{
var sourceField = $"{QuoteSqlIdentifier(tableAlias)}.{QuoteSqlIdentifier(column.Field.FieldName)}";
var cases = column.StaticLookupValues
.Select(item => $"WHEN {ToSqlLiteral(item.Key)} THEN {ToSqlLiteral(item.Value)}");
return $"CASE {sourceField} {string.Join(" ", cases)} ELSE CAST({sourceField} AS VARCHAR(4000)) END AS {QuoteSqlIdentifier(column.BindingName)}";
}
private static int FindClauseIndex(string sql, string clause) private static int FindClauseIndex(string sql, string clause)
{ {
return sql.IndexOf(clause, StringComparison.OrdinalIgnoreCase); return sql.IndexOf(clause, StringComparison.OrdinalIgnoreCase);
@ -630,7 +670,7 @@ public class DynamicGridReport : XtraReport
var query = lookupQuery.Trim().TrimEnd(';'); var query = lookupQuery.Trim().TrimEnd(';');
return Regex.Replace( return Regex.Replace(
query, query,
@"\s+ORDER\s+BY\s+[\s\S]*$", @"(?<![A-Za-z0-9_])ORDER\s+BY\b[\s\S]*$",
string.Empty, string.Empty,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
} }

View file

@ -69,7 +69,7 @@ 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 encoded gelse bile sadece rapor adını kontrol et. // Query string veya path parametreleri gelse bile sadece rapor adını kontrol et.
var (reportName, _) = ParseReportUrl(url); var (reportName, _) = ParseReportUrl(url);
return Path.GetFileName(reportName) == reportName; return Path.GetFileName(reportName) == reportName;
} }
@ -172,9 +172,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
return ms.ToArray(); return ms.ToArray();
} }
} }
catch (Exception) catch (Exception ex)
{ {
throw new FaultException(new FaultReason("Could not get report data."), new FaultCode("Server"), "GetData"); var message = ex.GetBaseException().Message;
throw new FaultException(new FaultReason($"Could not get report data. {message}"), new FaultCode("Server"), "GetData");
} }
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");
} }
@ -182,16 +183,12 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url) private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url)
{ {
var decodedUrl = DecodeRepeatedly(url); var decodedUrl = DecodeRepeatedly(url);
var reportName = decodedUrl;
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var queryIndex = decodedUrl.IndexOf('?'); var queryIndex = decodedUrl.IndexOf('?');
if (queryIndex < 0) var reportPath = queryIndex < 0 ? decodedUrl : decodedUrl[..queryIndex];
if (queryIndex >= 0)
{ {
return (reportName, parameters);
}
reportName = decodedUrl[..queryIndex];
var queryString = decodedUrl[(queryIndex + 1)..]; var queryString = decodedUrl[(queryIndex + 1)..];
var paramPairs = queryString.Split('&', StringSplitOptions.RemoveEmptyEntries); var paramPairs = queryString.Split('&', StringSplitOptions.RemoveEmptyEntries);
@ -203,10 +200,50 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
parameters[keyValue[0]] = keyValue[1]; parameters[keyValue[0]] = keyValue[1];
} }
} }
}
var pathParts = reportPath
.Split('/', StringSplitOptions.RemoveEmptyEntries)
.Select(Uri.UnescapeDataString)
.ToArray();
if (pathParts.Length == 0)
{
return (reportPath, parameters);
}
var reportName = pathParts[0];
if (pathParts.Length >= 3 && IsReportActionSegment(pathParts[1]))
{
AddPathParameters(reportName, pathParts.Skip(2).ToArray(), parameters);
}
return (reportName, parameters); return (reportName, parameters);
} }
private static void AddPathParameters(string reportName, string[] pathParameters, Dictionary<string, string> parameters)
{
if (pathParameters.Length == 0)
{
return;
}
if (IsDynamicGridReport(reportName) || IsDynamicFormReport(reportName))
{
if (pathParameters.Length == 1)
{
parameters.TryAdd("listFormCode", pathParameters[0]);
return;
}
parameters.TryAdd("id", pathParameters[0]);
parameters.TryAdd("listFormCode", pathParameters[1]);
return;
}
parameters.TryAdd("id", pathParameters[0]);
}
private static bool IsDynamicGridReport(string reportName) private static bool IsDynamicGridReport(string reportName)
{ {
return string.Equals(reportName, "DynamicGrid", StringComparison.OrdinalIgnoreCase) || return string.Equals(reportName, "DynamicGrid", StringComparison.OrdinalIgnoreCase) ||
@ -219,6 +256,12 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
string.Equals(reportName, Sozsoft.Reports.PredefinedReports.DynamicFormReport.ReportName, StringComparison.OrdinalIgnoreCase); string.Equals(reportName, Sozsoft.Reports.PredefinedReports.DynamicFormReport.ReportName, StringComparison.OrdinalIgnoreCase);
} }
private static bool IsReportActionSegment(string value)
{
return string.Equals(value, "view", StringComparison.OrdinalIgnoreCase) ||
string.Equals(value, "design", StringComparison.OrdinalIgnoreCase);
}
private static bool HasParameter(Dictionary<string, string> parameters, string name) private static bool HasParameter(Dictionary<string, string> parameters, string name)
{ {
return parameters.TryGetValue(name, out var value) && !string.IsNullOrWhiteSpace(value); return parameters.TryGetValue(name, out var value) && !string.IsNullOrWhiteSpace(value);

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: {
view: '/admin/reports/:id/view', view: '/admin/reports/:report/view/:id?/:listFormCode?',
design: '/admin/reports/:id/design', design: '/admin/reports/:report/design/:id?/:listFormCode?',
}, },
}, },
admin: { admin: {

View file

@ -11,6 +11,7 @@ import {
FaEye, FaEye,
FaSave, FaSave,
FaCog, FaCog,
FaFileAlt,
} from 'react-icons/fa' } from 'react-icons/fa'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { GridColumnData } from '../list/GridColumnData' import { GridColumnData } from '../list/GridColumnData'
@ -80,6 +81,13 @@ const FormButtons = (props: {
layout, layout,
}) })
const openDynamicGridReport = () => {
if (!id) return
const reportUrl = `/admin/reports/DynamicGrid/view/${encodeURIComponent(id)}/${encodeURIComponent(listFormCode)}`
window.open(reportUrl, isPwaMode ? '_self' : '_blank')
}
const handleDelete = async (e: any) => { const handleDelete = async (e: any) => {
//auth check //auth check
e.preventDefault() e.preventDefault()
@ -279,7 +287,17 @@ const FormButtons = (props: {
{...(permissions.c || permissions.u ? {} : { disabled: true })} {...(permissions.c || permissions.u ? {} : { disabled: true })}
></Button> ></Button>
)} )}
{checkPermission(gridDto?.gridOptions.permissionDto.c) && !isSubForm && ( {id && !isSubForm && (
<Button
size="sm"
variant="default"
className="dx-button dx-button-mode-contained dx-button-normal"
icon={<FaFileAlt />}
title={translate('::App.Reports.Form')}
onClick={openDynamicGridReport}
></Button>
)}
{checkPermission(gridDto?.gridOptions.permissionDto.u) && !isSubForm && (
<Button <Button
size="sm" size="sm"
variant="default" variant="default"

View file

@ -251,11 +251,20 @@ const useFilters = ({
} }
const menus = [] const menus = []
const openDynamicGridReport = () => {
const reportUrl = `/admin/reports/DynamicGrid/view/${encodeURIComponent(listFormCode)}`
window.open(reportUrl, isPwaMode ? '_self' : '_blank')
}
// #region Save/Reset grid state and Filters // #region Save/Reset grid state and Filters
if (grdOpt.filterRowDto?.allowUserUiFilterSave || grdOpt.stateStoringDto?.enabled) { if (grdOpt.filterRowDto?.allowUserUiFilterSave || grdOpt.stateStoringDto?.enabled) {
//Kullanicinin filtre islemi yapmasina izin verilmis ise filtreleme menu elemanlarini ekle //Kullanicinin filtre islemi yapmasina izin verilmis ise filtreleme menu elemanlarini ekle
if (grdOpt?.filterRowDto?.allowUserUiFilterSave) { if (grdOpt?.filterRowDto?.allowUserUiFilterSave) {
menus.push({
text: translate('::App.Reports.List'),
id: 'openDynamicGridReport',
icon: 'doc',
})
menus.push({ menus.push({
text: translate('::ListForms.ListForm.SaveFilter'), text: translate('::ListForms.ListForm.SaveFilter'),
id: 'saveActiveFilter', id: 'saveActiveFilter',
@ -410,6 +419,8 @@ const useFilters = ({
setIsImportModalOpen(true) setIsImportModalOpen(true)
} else if (itemData.id === 'permissionGrantedUsers') { } else if (itemData.id === 'permissionGrantedUsers') {
setIsPermissionUsersModalOpen(true) setIsPermissionUsersModalOpen(true)
} else if (itemData.id === 'openDynamicGridReport') {
openDynamicGridReport()
} }
}, },
// filtre menüsündeki elemanlar // filtre menüsündeki elemanlar

View file

@ -2,9 +2,10 @@ import React, { lazy, Suspense, useEffect } 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 } from 'react-router-dom' import { useLocation, useParams } from 'react-router-dom'
import { RequestOptions } from 'devexpress-reporting-react/dx-report-designer' import { RequestOptions } from 'devexpress-reporting-react/dx-report-designer'
import { APP_NAME } from '@/constants/app.constant' import { APP_NAME } from '@/constants/app.constant'
import { createReportUrl } from './reportRouteParams'
const ReportDesigner = lazy(() => const ReportDesigner = lazy(() =>
import('devexpress-reporting-react/dx-report-designer') import('devexpress-reporting-react/dx-report-designer')
@ -30,13 +31,19 @@ const loadDesignerCss = () => {
const DevexpressReportDesigner: React.FC = () => { const DevexpressReportDesigner: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { id } = useParams<{ id: string }>() const { report, id, listFormCode } = useParams<{
report: string
id: string
listFormCode: string
}>()
const location = useLocation()
const reportUrlWithParams = createReportUrl({ report, id, listFormCode }, location.search, 'design')
useEffect(() => { useEffect(() => {
loadDesignerCss() loadDesignerCss()
}, []) }, [])
if (!id) return null if (!reportUrlWithParams) return null
return ( return (
<Container> <Container>
@ -47,7 +54,7 @@ const DevexpressReportDesigner: React.FC = () => {
/> />
<Suspense fallback={<div>Rapor tasarımcısı yükleniyor...</div>}> <Suspense fallback={<div>Rapor tasarımcısı yükleniyor...</div>}>
<ReportDesigner reportUrl={id}> <ReportDesigner reportUrl={reportUrlWithParams}>
<RequestOptions <RequestOptions
host={`${import.meta.env.VITE_API_URL}/`} host={`${import.meta.env.VITE_API_URL}/`}
getDesignerModelAction="DXXRD/GetDesignerModel" getDesignerModelAction="DXXRD/GetDesignerModel"

View file

@ -10,6 +10,7 @@ import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native'
import { APP_NAME } from '@/constants/app.constant' import { APP_NAME } from '@/constants/app.constant'
import { MODE_DARK } from '@/constants/theme.constant' import { MODE_DARK } from '@/constants/theme.constant'
import { useStoreState } from '@/store' import { useStoreState } from '@/store'
import { createReportUrl } from './reportRouteParams'
const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme'
const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides' const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides'
@ -84,17 +85,16 @@ const loadViewerCss = (isDarkMode: boolean) => {
const DevexpressReportViewer: React.FC = () => { const DevexpressReportViewer: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { id, listFormCode, recordId } = useParams<{ const { report, listFormCode, id } = useParams<{
id: string report: string
listFormCode: string listFormCode: string
recordId: string id: string
}>() }>()
const location = useLocation() const location = useLocation()
const token = useStoreState((state) => state.auth.session.token) const token = useStoreState((state) => state.auth.session.token)
const themeMode = useStoreState((state) => state.theme.mode) const themeMode = useStoreState((state) => state.theme.mode)
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false) const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
const isDarkMode = themeMode === MODE_DARK const isDarkMode = themeMode === MODE_DARK
const formRecordId = recordId ?? (listFormCode ? id : undefined)
useEffect(() => { useEffect(() => {
loadViewerCss(isDarkMode) loadViewerCss(isDarkMode)
@ -121,22 +121,8 @@ const DevexpressReportViewer: React.FC = () => {
}, [token]) }, [token])
const reportUrlWithParams = useMemo(() => { const reportUrlWithParams = useMemo(() => {
const searchParams = new URLSearchParams(location.search) return createReportUrl({ report, id, listFormCode }, location.search)
}, [report, id, listFormCode, location.search])
if (listFormCode) {
searchParams.set('listFormCode', listFormCode)
if (formRecordId) {
searchParams.set('id', formRecordId)
return `DynamicFormReport?${searchParams.toString()}`
}
return `DynamicListFormReport?${searchParams.toString()}`
}
if (!id) return ''
return location.search ? `${id}${location.search}` : id
}, [id, listFormCode, formRecordId, location.search])
if (!reportUrlWithParams) return null if (!reportUrlWithParams) return null

View file

@ -0,0 +1,49 @@
export type ReportRouteParams = {
report?: string
id?: string
listFormCode?: string
}
const isDynamicGridReport = (report?: string) =>
report === 'DynamicGrid' || report === 'DynamicGridReport'
export const normalizeReportRouteParams = ({
report,
id,
listFormCode,
}: ReportRouteParams) => {
if (isDynamicGridReport(report) && id && !listFormCode) {
return {
report,
id: undefined,
listFormCode: id,
}
}
return {
report,
id,
listFormCode,
}
}
export const createReportUrl = (
params: ReportRouteParams,
search: string,
action = 'view',
) => {
const { report, id, listFormCode } = normalizeReportRouteParams(params)
if (!report) return ''
const searchParams = new URLSearchParams(search)
searchParams.delete('id')
searchParams.delete('listFormCode')
const pathSegments = [report, action]
if (id) pathSegments.push(id)
if (listFormCode) pathSegments.push(listFormCode)
const path = pathSegments.map((segment) => encodeURIComponent(segment)).join('/')
const queryString = searchParams.toString()
return queryString ? `${path}?${queryString}` : path
}