Demo ve Raporun ilk seferde açılamamasını çözümledim

This commit is contained in:
Sedat ÖZTÜRK 2026-06-29 10:13:58 +03:00
parent 352cf047b0
commit 9689ef1bc6
5 changed files with 97 additions and 14 deletions

View file

@ -3,9 +3,14 @@ using System.IO;
using System.Linq;
using System;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Sql;
using DevExpress.XtraPrinting;
using Microsoft.Extensions.DependencyInjection;
using Sozsoft.Platform.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users;
namespace Sozsoft.Platform.ReportServices;
@ -186,10 +191,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
var storedReportName = GetStoredReportName(reportName, parameters);
var isDynamicReport = IsDynamicReport(reportName);
var reportData = TryGetStoredReportData(storedReportName, parameters, isDynamicReport);
var reportData = await TryGetStoredReportDataAsync(storedReportName, parameters, isDynamicReport);
if (reportData == null && !string.Equals(storedReportName, reportName, StringComparison.OrdinalIgnoreCase))
{
reportData = TryGetStoredReportData(reportName, parameters, isDynamicReport);
reportData = await TryGetStoredReportDataAsync(reportName, parameters, isDynamicReport);
}
if (reportData != null)
{
@ -204,7 +209,7 @@ 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(
private async Task<byte[]> TryGetStoredReportDataAsync(
string reportName,
Dictionary<string, string> parameters,
bool isDynamicReport)
@ -215,14 +220,66 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
return null;
}
ApplyReportParameters(report, parameters);
if (isDynamicReport)
{
await EmbedDynamicReportDataAsync(report, parameters);
ApplyDynamicReportFooter(report);
}
else
{
ApplyReportParameters(report, parameters);
}
return SaveReportLayout(report);
}
private async Task EmbedDynamicReportDataAsync(
XtraReport report,
IReadOnlyDictionary<string, string> parameters)
{
if (!parameters.TryGetValue("id", out var idValue) || !Guid.TryParse(idValue, out var reportTemplateId))
{
throw new InvalidOperationException("A valid report template id is required.");
}
using var scope = _serviceProvider.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<IRepository<ReportTemplate, Guid>>();
var template = await repository.FindAsync(reportTemplateId);
if (template == null)
{
throw new InvalidOperationException($"Report template '{reportTemplateId}' was not found.");
}
// A stored DynamicReport layout can contain a named SqlDataSource. The cached document
// builder executes that source later on a background thread, where its connection can be
// stale or no longer carry the request's tenant context. Embed the tenant-scoped result in
// the layout so document generation no longer opens a second database connection.
foreach (var sqlDataSource in report.ComponentStorage.OfType<SqlDataSource>().ToList())
{
if (ReferenceEquals(report.DataSource, sqlDataSource))
{
report.DataSource = null;
}
report.ComponentStorage.Remove(sqlDataSource);
sqlDataSource.Dispose();
}
var dataSource = new JsonDataSource
{
Name = "DynamicReportData",
JsonSource = new CustomJsonSource(JsonSerializer.Serialize(new[]
{
new { template.HtmlContent }
}))
};
dataSource.Fill();
report.ComponentStorage.Add(dataSource);
report.DataSource = dataSource;
report.DataMember = null;
}
private void ApplyDynamicReportFooter(XtraReport report)
{
var bottomMargin = report.Bands.OfType<BottomMarginBand>().FirstOrDefault();

View file

@ -22,6 +22,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
import LanguageSelector from '../template/LanguageSelector'
import Demo from '@/views/public/Demo'
import { ScrollContext } from '@/contexts/ScrollContext'
import { DemoProvider } from '@/contexts/DemoContext'
import useDarkMode from '@/utils/hooks/useDarkmode'
import { THEME_ENUM } from '@/constants/theme.constant'
import { Button } from '../ui'
@ -170,8 +171,9 @@ const PublicLayout = () => {
}
return (
<ScrollContext.Provider value={scrolled}>
<div className="flex flex-col min-h-screen">
<DemoProvider value={{ openDemo: () => setIsDemoOpen(true) }}>
<ScrollContext.Provider value={scrolled}>
<div className="flex flex-col min-h-screen">
{/* HEADER */}
<header
className={`fixed w-full z-50 transition-all duration-500 ${
@ -607,9 +609,10 @@ const PublicLayout = () => {
</footer>
{/* Demo Modal */}
<Demo isOpen={isDemoOpen} onClose={() => setIsDemoOpen(false)} />
</div>
</ScrollContext.Provider>
<Demo isOpen={isDemoOpen} onClose={() => setIsDemoOpen(false)} />
</div>
</ScrollContext.Provider>
</DemoProvider>
)
}

View file

@ -0,0 +1,19 @@
import React, { createContext, useContext } from 'react'
interface DemoContextValue {
openDemo: () => void
}
const DemoContext = createContext<DemoContextValue | undefined>(undefined)
export const DemoProvider = DemoContext.Provider
export const useDemo = () => {
const context = useContext(DemoContext)
if (!context) {
throw new Error('useDemo must be used within a DemoProvider')
}
return context
}

View file

@ -167,9 +167,10 @@ const Demo: React.FC<DemoModalProps> = ({ isOpen, onClose }) => {
onClick={onClose}
title="Kapat"
aria-label="Kapat"
variant="plain"
variant="default"
shape="circle"
className="absolute top-4 right-4 !h-8 !w-8 !px-0 text-2xl text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100"
size="lg"
className="absolute top-4 right-4 !h-8 !w-8 !px-0 bg-gray-900 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100"
>
&times;
</Button>

View file

@ -18,6 +18,7 @@ import { DesignerSelection } from './designer/types'
import { useDesignerState } from './designer/useDesignerState'
import { getHome, HomeDto, saveHomePage } from '@/services/home.service'
import { Button, Notification, toast } from '@/components/ui'
import { useDemo } from '@/contexts/DemoContext'
interface HomeSlideServiceContent {
icon: string
@ -272,6 +273,7 @@ function buildHomeContent(home: HomeDto | undefined, translate: (key: string) =>
}
const Home: React.FC = () => {
const { openDemo } = useDemo()
const { translate } = useLocalization()
const { setLang } = useStoreActions((actions) => actions.locale)
const { getConfig } = useStoreActions((actions) => actions.abpConfig)
@ -988,13 +990,14 @@ const Home: React.FC = () => {
</p>
<div className="flex flex-col md:flex-row justify-center gap-6 mb-16">
<Link
to={ROUTES_ENUM.public.contact}
<button
type="button"
onClick={openDemo}
className={content?.heroPrimaryCtaStyle}
>
{content?.heroPrimaryCtaLabel}{' '}
<FaArrowRight className="ml-2" size={20} />
</Link>
</button>
<Link
to={ROUTES_ENUM.public.products}
className={content?.heroSecondaryCtaStyle}