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.Linq;
using System; using System;
using System.Reflection; using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Sql;
using DevExpress.XtraPrinting; using DevExpress.XtraPrinting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Sozsoft.Platform.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users; using Volo.Abp.Users;
namespace Sozsoft.Platform.ReportServices; namespace Sozsoft.Platform.ReportServices;
@ -186,10 +191,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
var storedReportName = GetStoredReportName(reportName, parameters); var storedReportName = GetStoredReportName(reportName, parameters);
var isDynamicReport = IsDynamicReport(reportName); 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)) if (reportData == null && !string.Equals(storedReportName, reportName, StringComparison.OrdinalIgnoreCase))
{ {
reportData = TryGetStoredReportData(reportName, parameters, isDynamicReport); reportData = await TryGetStoredReportDataAsync(reportName, parameters, isDynamicReport);
} }
if (reportData != null) 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"); 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, string reportName,
Dictionary<string, string> parameters, Dictionary<string, string> parameters,
bool isDynamicReport) bool isDynamicReport)
@ -215,14 +220,66 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
return null; return null;
} }
ApplyReportParameters(report, parameters);
if (isDynamicReport) if (isDynamicReport)
{ {
await EmbedDynamicReportDataAsync(report, parameters);
ApplyDynamicReportFooter(report); ApplyDynamicReportFooter(report);
} }
else
{
ApplyReportParameters(report, parameters);
}
return SaveReportLayout(report); 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) private void ApplyDynamicReportFooter(XtraReport report)
{ {
var bottomMargin = report.Bands.OfType<BottomMarginBand>().FirstOrDefault(); 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 LanguageSelector from '../template/LanguageSelector'
import Demo from '@/views/public/Demo' import Demo from '@/views/public/Demo'
import { ScrollContext } from '@/contexts/ScrollContext' import { ScrollContext } from '@/contexts/ScrollContext'
import { DemoProvider } from '@/contexts/DemoContext'
import useDarkMode from '@/utils/hooks/useDarkmode' import useDarkMode from '@/utils/hooks/useDarkmode'
import { THEME_ENUM } from '@/constants/theme.constant' import { THEME_ENUM } from '@/constants/theme.constant'
import { Button } from '../ui' import { Button } from '../ui'
@ -170,8 +171,9 @@ const PublicLayout = () => {
} }
return ( return (
<ScrollContext.Provider value={scrolled}> <DemoProvider value={{ openDemo: () => setIsDemoOpen(true) }}>
<div className="flex flex-col min-h-screen"> <ScrollContext.Provider value={scrolled}>
<div className="flex flex-col min-h-screen">
{/* HEADER */} {/* HEADER */}
<header <header
className={`fixed w-full z-50 transition-all duration-500 ${ className={`fixed w-full z-50 transition-all duration-500 ${
@ -607,9 +609,10 @@ const PublicLayout = () => {
</footer> </footer>
{/* Demo Modal */} {/* Demo Modal */}
<Demo isOpen={isDemoOpen} onClose={() => setIsDemoOpen(false)} /> <Demo isOpen={isDemoOpen} onClose={() => setIsDemoOpen(false)} />
</div> </div>
</ScrollContext.Provider> </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} onClick={onClose}
title="Kapat" title="Kapat"
aria-label="Kapat" aria-label="Kapat"
variant="plain" variant="default"
shape="circle" 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; &times;
</Button> </Button>

View file

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