diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 2af5040..16b69ae 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -1231,8 +1231,6 @@ public class PlatformDbContext : b.Property(x => x.SubmissionTime).IsRequired(); - b.HasIndex(x => new { x.SurveyId, x.UserId }).IsUnique().HasFilter("[IsDeleted] = 0"); - b.HasOne(x => x.Survey) .WithMany(x => x.SurveyResponses) .HasForeignKey(x => x.SurveyId) diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.Designer.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.Designer.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.Designer.cs index 938a71d..bddb82d 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Sozsoft.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20260619185755_Initial")] + [Migration("20260622213308_Initial")] partial class Initial { /// @@ -5470,9 +5470,7 @@ namespace Sozsoft.Platform.Migrations b.HasKey("Id"); - b.HasIndex("SurveyId", "UserId") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); + b.HasIndex("SurveyId"); b.ToTable("Adm_T_SurveyResponse", (string)null); }); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.cs index 003a7a4..5613d37 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260619185755_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260622213308_Initial.cs @@ -3808,11 +3808,9 @@ namespace Sozsoft.Platform.Migrations column: "QuestionId"); migrationBuilder.CreateIndex( - name: "IX_Adm_T_SurveyResponse_SurveyId_UserId", + name: "IX_Adm_T_SurveyResponse_SurveyId", table: "Adm_T_SurveyResponse", - columns: new[] { "SurveyId", "UserId" }, - unique: true, - filter: "[IsDeleted] = 0"); + column: "SurveyId"); migrationBuilder.CreateIndex( name: "IX_Adm_T_Videoroom_TenantId_BranchId_Name", diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index eba8393..ff51bb1 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -5467,9 +5467,7 @@ namespace Sozsoft.Platform.Migrations b.HasKey("Id"); - b.HasIndex("SurveyId", "UserId") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); + b.HasIndex("SurveyId"); b.ToTable("Adm_T_SurveyResponse", (string)null); }); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs index e24ab21..a32d473 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs @@ -28,7 +28,7 @@ public class DynamicFormReport : XtraReport { public const string ReportName = nameof(DynamicFormReport); private const string QueryName = "DynamicForm"; - private const int HorizontalMargin = 35; + private const int HorizontalMargin = 50; private const float RowHeight = 24; private const float ColumnGap = 10; private static readonly System.Drawing.Color FieldBorderColor = System.Drawing.Color.FromArgb(226, 232, 240); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs index bcd2171..00b5a18 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs @@ -27,7 +27,7 @@ public class DynamicGridReport : XtraReport public const string ReportName = nameof(DynamicGridReport); private const string QueryName = "DynamicGrid"; private const int A4PortraitWidth = 827; - private const int HorizontalMargin = 35; + private const int HorizontalMargin = 50; private const int LandscapeColumnThreshold = 5; private static readonly System.Drawing.Color TableBorderColor = System.Drawing.Color.FromArgb(226, 232, 240); private static IServiceProvider _serviceProvider; diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.Designer.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.Designer.cs index ed55324..73df6cd 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.Designer.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.Designer.cs @@ -39,7 +39,7 @@ namespace Sozsoft.Reports.PredefinedReports System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DynamicReport)); this.TopMargin = new DevExpress.XtraReports.UI.TopMarginBand(); this.BottomMargin = new DevExpress.XtraReports.UI.BottomMarginBand(); - this.pageInfo1 = new DevExpress.XtraReports.UI.XRPageInfo(); + this.currentUserNameLabel = new DevExpress.XtraReports.UI.XRLabel(); this.pageInfo2 = new DevExpress.XtraReports.UI.XRPageInfo(); this.ReportHeader = new DevExpress.XtraReports.UI.ReportHeaderBand(); this.label1 = new DevExpress.XtraReports.UI.XRLabel(); @@ -55,6 +55,10 @@ namespace Sozsoft.Reports.PredefinedReports this.PageInfo = new DevExpress.XtraReports.UI.XRControlStyle(); ((System.ComponentModel.ISupportInitialize)(this.xrRichText1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this)).BeginInit(); + // + // ReportHeader + // + this.ReportHeader.HeightF = 35F; // // TopMargin // @@ -63,17 +67,16 @@ namespace Sozsoft.Reports.PredefinedReports // BottomMargin // this.BottomMargin.Controls.AddRange(new DevExpress.XtraReports.UI.XRControl[] { - this.pageInfo1, + this.currentUserNameLabel, this.pageInfo2}); this.BottomMargin.Name = "BottomMargin"; // - // pageInfo1 + // currentUserNameLabel // - this.pageInfo1.LocationFloat = new DevExpress.Utils.PointFloat(0F, 0F); - this.pageInfo1.Name = "pageInfo1"; - this.pageInfo1.PageInfo = DevExpress.XtraPrinting.PageInfo.DateTime; - this.pageInfo1.SizeF = new System.Drawing.SizeF(325F, 23F); - this.pageInfo1.StyleName = "PageInfo"; + this.currentUserNameLabel.LocationFloat = new DevExpress.Utils.PointFloat(0F, 0F); + this.currentUserNameLabel.Name = "currentUserNameLabel"; + this.currentUserNameLabel.SizeF = new System.Drawing.SizeF(325F, 23F); + this.currentUserNameLabel.StyleName = "PageInfo"; // // pageInfo2 // @@ -196,7 +199,7 @@ namespace Sozsoft.Reports.PredefinedReports private DevExpress.XtraReports.UI.TopMarginBand TopMargin; private DevExpress.XtraReports.UI.BottomMarginBand BottomMargin; - private DevExpress.XtraReports.UI.XRPageInfo pageInfo1; + private DevExpress.XtraReports.UI.XRLabel currentUserNameLabel; private DevExpress.XtraReports.UI.XRPageInfo pageInfo2; private DevExpress.XtraReports.UI.ReportHeaderBand ReportHeader; private DevExpress.XtraReports.UI.XRLabel label1; diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.cs index 8fa2a13..3dfd2f2 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReport.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using DevExpress.Drawing; +using DevExpress.XtraPrinting; using DevExpress.XtraReports.UI; using Sozsoft.Languages.Entities; using Sozsoft.Platform.EntityFrameworkCore; @@ -12,6 +14,9 @@ namespace Sozsoft.Reports.PredefinedReports; public partial class DynamicReport : XtraReport { + private const int HorizontalMargin = 50; + private const int VerticalMargin = 35; + private Dictionary _parameters; private static IServiceProvider _serviceProvider; @@ -27,10 +32,7 @@ public partial class DynamicReport : XtraReport { InitializeComponent(); - // A4 kağıt ayarları - this.PageHeight = 1169; - this.PageWidth = 827; - this.Landscape = false; + ConfigurePage(); _parameters = new Dictionary(); @@ -40,15 +42,39 @@ public partial class DynamicReport : XtraReport AddFooterCurrentUserName(); AddFooterLine(); - pageInfo1.Visible = false; xrRichText1.BeforePrint += XrRichText1_BeforePrint; } +#pragma warning disable CA1416 + private void ConfigurePage() + { + PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4; + Landscape = false; + Margins = new System.Drawing.Printing.Margins( + HorizontalMargin, + HorizontalMargin, + VerticalMargin, + VerticalMargin); + Font = new DXFont("Arial", 8.25F); + + TopMargin.HeightF = Margins.Top; + BottomMargin.HeightF = Margins.Bottom; + + var pageWidth = PageWidth - Margins.Left - Margins.Right; + xrRichText1.WidthF = pageWidth; + currentUserNameLabel.LocationFloat = new DevExpress.Utils.PointFloat(0, 8); + currentUserNameLabel.SizeF = new System.Drawing.SizeF(pageWidth / 2, 18); + currentUserNameLabel.TextAlignment = TextAlignment.MiddleLeft; + pageInfo2.LocationFloat = new DevExpress.Utils.PointFloat(pageWidth / 2, 8); + pageInfo2.SizeF = new System.Drawing.SizeF(pageWidth / 2, 18); + pageInfo2.TextAlignment = TextAlignment.MiddleRight; + } +#pragma warning restore CA1416 + private void AddFooterLine() { var pageWidth = PageWidth - Margins.Left - Margins.Right; DynamicReportImageHelper.AddFooterTopLine(BottomMargin.Controls, pageWidth); - pageInfo1.TopF = 8; pageInfo2.TopF = 8; } @@ -69,8 +95,7 @@ public partial class DynamicReport : XtraReport using var scope = _serviceProvider.CreateScope(); var currentUser = scope.ServiceProvider.GetRequiredService(); - var pageWidth = PageWidth - Margins.Left - Margins.Right; - DynamicReportImageHelper.AddCurrentUserNameToFooter(BottomMargin.Controls, currentUser.UserName, pageWidth); + currentUserNameLabel.Text = currentUser.UserName ?? string.Empty; } catch { diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs index b6cb6c7..7be7aa3 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs @@ -27,7 +27,7 @@ public class DynamicTreeReport : XtraReport { public const string ReportName = nameof(DynamicTreeReport); private const int A4PortraitWidth = 827; - private const int HorizontalMargin = 35; + private const int HorizontalMargin = 50; private const int LandscapeColumnThreshold = 5; private const string TreeLevelColumn = "__TreeLevel"; private const string TreeHasChildrenColumn = "__TreeHasChildren"; diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/ReportServices/CustomReportStorageWebExtension.cs b/api/src/Sozsoft.Platform.HttpApi.Host/ReportServices/CustomReportStorageWebExtension.cs index 4cd2fb9..e993f7d 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/ReportServices/CustomReportStorageWebExtension.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/ReportServices/CustomReportStorageWebExtension.cs @@ -4,6 +4,9 @@ using System.Linq; using System; using System.Reflection; using System.Threading.Tasks; +using DevExpress.XtraPrinting; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Users; namespace Sozsoft.Platform.ReportServices; @@ -48,11 +51,11 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens this.reportDirectory = reportDirectory; } - private bool IsWithinReportsFolder(string url, string folder) + private static bool IsSafeReportFileName(string reportName) { - var rootDirectory = new DirectoryInfo(folder); - var fileInfo = new FileInfo(Path.Combine(folder, url)); - return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower()); + return !string.IsNullOrWhiteSpace(reportName) && + Path.GetFileName(reportName) == reportName && + reportName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0; } public override bool CanSetData(string url) @@ -72,8 +75,8 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens // Return **true** if no validation is required. // Query string veya path parametreleri gelse bile sadece rapor adını kontrol et. - var (reportName, _) = ParseReportUrl(url); - return Path.GetFileName(reportName) == reportName; + var reportName = GetStorageReportName(url); + return IsSafeReportFileName(reportName); } public override byte[] GetData(string url) @@ -181,7 +184,13 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens return SaveReportLayout(report); } - var reportData = TryGetStoredReportData(reportName, parameters); + var storedReportName = GetStoredReportName(reportName, parameters); + var isDynamicReport = IsDynamicReport(reportName); + var reportData = TryGetStoredReportData(storedReportName, parameters, isDynamicReport); + if (reportData == null && !string.Equals(storedReportName, reportName, StringComparison.OrdinalIgnoreCase)) + { + reportData = TryGetStoredReportData(reportName, parameters, isDynamicReport); + } if (reportData != null) { return reportData; @@ -195,7 +204,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData"); } - private byte[] TryGetStoredReportData(string reportName, Dictionary parameters) + private byte[] TryGetStoredReportData( + string reportName, + Dictionary parameters, + bool isDynamicReport) { var report = TryLoadStoredReport(reportName); if (report == null) @@ -204,9 +216,52 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens } ApplyReportParameters(report, parameters); + if (isDynamicReport) + { + ApplyDynamicReportFooter(report); + } return SaveReportLayout(report); } + private void ApplyDynamicReportFooter(XtraReport report) + { + var bottomMargin = report.Bands.OfType().FirstOrDefault(); + if (bottomMargin == null) + { + return; + } + + var pageWidth = report.PageWidth - report.Margins.Left - report.Margins.Right; + var legacyDateTime = bottomMargin.Controls + .OfType() + .FirstOrDefault(control => + string.Equals(control.Name, "pageInfo1", StringComparison.OrdinalIgnoreCase) && + control.PageInfo == PageInfo.DateTime); + if (legacyDateTime != null) + { + legacyDateTime.Visible = false; + } + + var userNameLabel = bottomMargin.Controls + .OfType() + .FirstOrDefault(control => + string.Equals(control.Name, "currentUserNameLabel", StringComparison.OrdinalIgnoreCase)); + if (userNameLabel == null) + { + userNameLabel = new XRLabel { Name = "currentUserNameLabel" }; + bottomMargin.Controls.Add(userNameLabel); + } + + using var scope = _serviceProvider.CreateScope(); + var currentUser = scope.ServiceProvider.GetService(); + userNameLabel.Text = currentUser?.Name ?? currentUser?.UserName ?? string.Empty; + userNameLabel.BoundsF = new System.Drawing.RectangleF(0, 8, pageWidth / 2, 18); + userNameLabel.Font = new DevExpress.Drawing.DXFont("Arial", 7.5F); + userNameLabel.TextAlignment = TextAlignment.MiddleLeft; + userNameLabel.CanGrow = false; + userNameLabel.Multiline = false; + } + private XtraReport TryLoadStoredReport(string reportName) { if (Directory.EnumerateFiles(reportDirectory).Select(Path.GetFileNameWithoutExtension).Contains(reportName)) @@ -329,6 +384,20 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens parameters.TryAdd("id", pathParameters[0]); } + private static string GetStorageReportName(string url) + { + var (reportName, parameters) = ParseReportUrl(url); + return GetStoredReportName(reportName, parameters); + } + + private static string GetStoredReportName(string reportName, Dictionary parameters) + { + return string.Equals(reportName, nameof(Sozsoft.Reports.PredefinedReports.DynamicReport), StringComparison.OrdinalIgnoreCase) && + HasParameter(parameters, "id") + ? parameters["id"] + : reportName; + } + private static bool IsDynamicGridReport(string reportName) { return string.Equals(reportName, "DynamicGrid", StringComparison.OrdinalIgnoreCase) || @@ -347,6 +416,14 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens string.Equals(reportName, Sozsoft.Reports.PredefinedReports.DynamicFormReport.ReportName, StringComparison.OrdinalIgnoreCase); } + private static bool IsDynamicReport(string reportName) + { + return string.Equals( + reportName, + nameof(Sozsoft.Reports.PredefinedReports.DynamicReport), + StringComparison.OrdinalIgnoreCase); + } + private static bool IsReportActionSegment(string value) { return string.Equals(value, "view", StringComparison.OrdinalIgnoreCase) || @@ -403,9 +480,12 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens { // Saves the specified report to the report storage with the specified name // (saves existing reports only). - if (!IsWithinReportsFolder(url, reportDirectory)) + var reportName = GetStorageReportName(url); + if (!IsSafeReportFileName(reportName)) throw new FaultException(new FaultReason("Invalid report name."), new FaultCode("Server"), "GetData"); - report.SaveLayoutToXml(Path.Combine(reportDirectory, url + FileExtension)); + + Directory.CreateDirectory(reportDirectory); + report.SaveLayoutToXml(Path.Combine(reportDirectory, reportName + FileExtension)); } public override string SetNewData(XtraReport report, string defaultUrl) diff --git a/ui/src/views/list/Chart.tsx b/ui/src/views/list/Chart.tsx index fe4d4f3..475e60a 100644 --- a/ui/src/views/list/Chart.tsx +++ b/ui/src/views/list/Chart.tsx @@ -26,6 +26,8 @@ import { } from '@/services/admin/list-form.service' import { layoutTypes } from '../admin/listForm/edit/types' import { useListFormCustomDataSource } from './useListFormCustomDataSource' +import { useListFormColumns } from './useListFormColumns' +import { GridColumnData } from './GridColumnData' import Toolbar, { Item } from 'devextreme-react/toolbar' interface ChartProps extends CommonProps, Meta { @@ -39,6 +41,8 @@ interface ChartProps extends CommonProps, Meta { refreshGridDto?: () => Promise } +type LookupDisplayValues = Record> + const Chart = (props: ChartProps) => { // State UserId güncellemesi için const { userName } = useStoreState((s) => s.auth.user) @@ -51,6 +55,12 @@ const Chart = (props: ChartProps) => { const [searchParams] = useSearchParams() const [chartOptions, setChartOptions] = useState() const { createSelectDataSource } = useListFormCustomDataSource({} as any) + const { loadLookupDisplayValues } = useListFormColumns({ + gridDto, + listFormCode, + isSubForm, + }) + const [lookupDisplayValues, setLookupDisplayValues] = useState({}) const params = useParams() const _listFormCode = props?.listFormCode ?? params?.listFormCode ?? '' @@ -94,6 +104,34 @@ const Chart = (props: ChartProps) => { const [allSeries, setAllSeries] = useState([]) + const lookupColumns = useMemo( + () => + gridDto?.columnFormats + .filter((column) => column.fieldName) + .map((column) => ({ dataField: column.fieldName, colData: column })) ?? [], + [gridDto?.columnFormats], + ) + + useEffect(() => { + let isActive = true + + const loadValues = async () => { + try { + const values = await loadLookupDisplayValues(lookupColumns) + if (isActive) { + setLookupDisplayValues(values) + } + } catch (error) { + console.error('Chart lookup load error:', error) + } + } + + loadValues() + return () => { + isActive = false + } + }, [lookupColumns, loadLookupDisplayValues]) + useEffect(() => { if (gridDto) { const initialSeries = gridDto.gridOptions.seriesDto.map((s, index) => ({ ...s, index })) @@ -134,6 +172,10 @@ const Chart = (props: ChartProps) => { layoutTypes.chart, ) + const argumentLookupValues = currentSeries + .map((series) => (series.argumentField ? lookupDisplayValues[series.argumentField] : undefined)) + .find((values) => values?.size) + return { dataSource: dataSource, @@ -165,7 +207,20 @@ const Chart = (props: ChartProps) => { animation: gridDto.gridOptions.animationDto, export: gridDto.gridOptions.exportDto, crosshair: gridDto.gridOptions.crosshairDto, - argumentAxis: gridDto.gridOptions.argumentAxisDto, + argumentAxis: { + ...gridDto.gridOptions.argumentAxisDto, + label: { + ...gridDto.gridOptions.argumentAxisDto?.label, + ...(argumentLookupValues + ? { + customizeText: (argument: any) => { + const value = argument.value ?? argument.valueText + return argumentLookupValues.get(String(value)) ?? argument.valueText ?? String(value ?? '') + }, + } + : {}), + }, + }, valueAxis: gridDto.gridOptions.valueAxisDto, tooltip: gridDto.gridOptions.tooltipDto || { enabled: true, @@ -194,6 +249,7 @@ const Chart = (props: ChartProps) => { listFormCode, urlSearchParams, openDrawer, + lookupDisplayValues, ]) useEffect(() => { diff --git a/ui/src/views/list/Pivot.tsx b/ui/src/views/list/Pivot.tsx index ce5f276..bfd00aa 100644 --- a/ui/src/views/list/Pivot.tsx +++ b/ui/src/views/list/Pivot.tsx @@ -55,6 +55,8 @@ interface PivotProps { const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk +type LookupDisplayValues = Record> + const Pivot = (props: PivotProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto } = props const { translate } = useLocalization() @@ -68,6 +70,7 @@ const Pivot = (props: PivotProps) => { const [gridDataSource, setGridDataSource] = useState>() const [columnData, setColumnData] = useState() + const [lookupDisplayValues, setLookupDisplayValues] = useState({}) const [showChart, setShowChart] = useState(false) // StateStoring için storageKey'i memoize et @@ -82,7 +85,7 @@ const Pivot = (props: PivotProps) => { }) const { createSelectDataSource } = useListFormCustomDataSource({ gridRef }) - const { getBandedColumns } = useListFormColumns({ + const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({ gridDto, listFormCode, isSubForm, @@ -355,6 +358,30 @@ const Pivot = (props: PivotProps) => { } }, [memoizedColumns]) + // PivotGrid, DataGrid'deki lookup ayarını doğrudan desteklemez. Lookup değerlerini + // yükleyip sadece görünen metni dönüştürüyoruz; gruplama/filtreleme gerçek value ile devam eder. + useEffect(() => { + let isActive = true + + const loadValues = async () => { + if (!columnData) return + + try { + const values = await loadLookupDisplayValues(columnData) + if (isActive) { + setLookupDisplayValues(values) + } + } catch (error) { + console.error('Pivot lookup load error:', error) + } + } + + loadValues() + return () => { + isActive = false + } + }, [columnData, loadLookupDisplayValues]) + useEffect(() => { if (memoizedDataSource) { setGridDataSource(memoizedDataSource) @@ -384,6 +411,7 @@ const Pivot = (props: PivotProps) => { // Default fields'i hazırla const defaultFields: any = columnData?.map((b) => { + const lookupValues = b.dataField ? lookupDisplayValues[b.dataField] : undefined return { dataField: b.dataField, caption: b.caption, @@ -397,6 +425,14 @@ const Pivot = (props: PivotProps) => { wordWrapEnabled: b.colData?.pivotSettingsDto.wordWrapEnabled, visible: true, width: b.width, + ...(lookupValues + ? { + customizeText: (cellInfo: any) => { + const value = cellInfo.value ?? cellInfo.valueText + return lookupValues.get(String(value)) ?? cellInfo.valueText ?? String(value ?? '') + }, + } + : {}), } as Field }) @@ -408,7 +444,7 @@ const Pivot = (props: PivotProps) => { } instance.option('dataSource', dataSource) - }, [columnData, gridDataSource, gridDto]) + }, [columnData, gridDataSource, gridDto, lookupDisplayValues]) // Component mount olduğunda state'i bir kez yükle useEffect(() => { diff --git a/ui/src/views/list/useListFormColumns.ts b/ui/src/views/list/useListFormColumns.ts index 52be303..8e25a33 100644 --- a/ui/src/views/list/useListFormColumns.ts +++ b/ui/src/views/list/useListFormColumns.ts @@ -435,6 +435,50 @@ const useListFormColumns = ({ [listFormCode], ) + const loadLookupDisplayValues = useCallback( + async (columns: GridColumnData[]) => { + const entries = await Promise.all( + columns + .filter( + (column) => + column.dataField && + column.colData?.lookupDto?.dataSourceType && + !column.colData.lookupDto.cascadeParentFields, + ) + .map(async (column) => { + const lookup = column.colData!.lookupDto + const source = lookupDataSource({}, column.colData, listFormCode) + const store = + source instanceof CustomStore + ? source + : (source as { store?: CustomStore } | undefined)?.store + const items = ((await store?.load?.()) ?? []) as Record[] + const values = new Map() + + items.forEach((item: Record) => { + const getValue = (expression?: string) => { + const key = Object.keys(item).find( + (itemKey) => itemKey.toLowerCase() === expression?.toLowerCase(), + ) + return key ? item[key] : undefined + } + const value = getValue(lookup.valueExpr) + const text = getValue(lookup.displayExpr) + + if (value !== undefined && value !== null && text !== undefined && text !== null) { + values.set(String(value), String(text)) + } + }) + + return [column.dataField!, values] as const + }), + ) + + return Object.fromEntries(entries) as Record> + }, + [listFormCode, lookupDataSource], + ) + const createLookupStaticDataSource = ( load: () => any, filter: any = null, @@ -1036,6 +1080,7 @@ const useListFormColumns = ({ return { getBandedColumns, + loadLookupDisplayValues, } } diff --git a/ui/src/views/report/DevexpressReportDesigner.tsx b/ui/src/views/report/DevexpressReportDesigner.tsx index 4f20293..9f7e20f 100644 --- a/ui/src/views/report/DevexpressReportDesigner.tsx +++ b/ui/src/views/report/DevexpressReportDesigner.tsx @@ -15,6 +15,7 @@ const ReportDesigner = lazy(() => const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides' +const REPORT_DESIGNER_CSS_ID = 'devexpress-report-designer-css' const getViewerClassName = (isDarkMode: boolean) => [ @@ -58,6 +59,17 @@ const loadViewerCss = (isDarkMode: boolean) => { if (themeLink.href !== themeHref) { themeLink.href = themeHref } + + if (!document.getElementById(REPORT_DESIGNER_CSS_ID)) { + const designerLink = document.createElement('link') + designerLink.id = REPORT_DESIGNER_CSS_ID + designerLink.rel = 'stylesheet' + designerLink.href = new URL( + 'devexpress-reporting/dist/css/dx-reportdesigner.css', + import.meta.url, + ).href + document.head.appendChild(designerLink) + } } const DevexpressReportDesigner: React.FC = () => {