sozsoft-platform/ui/src/views/report/DevexpressReportViewer.tsx

150 lines
4.7 KiB
TypeScript
Raw Normal View History

import React, { useMemo, useEffect, useState } from 'react'
2026-02-24 20:44:16 +00:00
import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useParams, useLocation } from 'react-router-dom'
import ReportViewer, {
RequestOptions,
} from 'devexpress-reporting-react/dx-report-viewer'
import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native'
2026-02-24 20:44:16 +00:00
import { APP_NAME } from '@/constants/app.constant'
import { MODE_DARK } from '@/constants/theme.constant'
import { useStoreState } from '@/store'
import { createReportUrl } from './reportRouteParams'
2026-02-24 20:44:16 +00:00
const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme'
const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides'
const getViewerClassName = (isDarkMode: boolean) =>
[
'dx-viewport',
'dx-device-desktop',
'dx-device-generic',
'dx-theme-generic',
'dx-theme-generic-typography',
isDarkMode ? 'dx-color-scheme-dark' : 'dx-color-scheme-light',
'report-viewer-shell',
isDarkMode ? 'report-viewer-shell-dark' : '',
]
.filter(Boolean)
.join(' ')
const loadViewerCss = (isDarkMode: boolean) => {
2026-02-24 20:44:16 +00:00
const styles = [
new URL('@devexpress/analytics-core/dist/css/dx-analytics.common.css', import.meta.url).href,
new URL('devexpress-reporting/dist/css/dx-webdocumentviewer.css', import.meta.url).href,
]
styles.forEach((href) => {
if (document.querySelector(`link[href="${href}"]`)) return
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
document.head.appendChild(link)
})
const themeHref = isDarkMode
? new URL('@devexpress/analytics-core/dist/css/dx-analytics.dark.css', import.meta.url).href
: new URL('@devexpress/analytics-core/dist/css/dx-analytics.light.css', import.meta.url).href
let themeLink = document.getElementById(VIEWER_THEME_LINK_ID) as HTMLLinkElement | null
if (!themeLink) {
themeLink = document.createElement('link')
themeLink.id = VIEWER_THEME_LINK_ID
themeLink.rel = 'stylesheet'
document.head.appendChild(themeLink)
}
if (themeLink.href !== themeHref) {
themeLink.href = themeHref
}
if (!document.getElementById(VIEWER_DARK_OVERRIDES_ID)) {
const style = document.createElement('style')
style.id = VIEWER_DARK_OVERRIDES_ID
style.textContent = `
.report-viewer-shell {
min-height: calc(100vh - 4rem);
}
.report-viewer-shell-dark {
background: #111827;
color: #e5e7eb;
}
.report-viewer-shell-dark .dxrd-preview,
.report-viewer-shell-dark .dxrd-preview-wrapper,
.report-viewer-shell-dark .dxrd-toolbar-wrapper,
.report-viewer-shell-dark .dxrd-right-panel,
.report-viewer-shell-dark .dxrd-search-wrapper {
background-color: #111827;
}
`
document.head.appendChild(style)
}
2026-02-24 20:44:16 +00:00
}
const DevexpressReportViewer: React.FC = () => {
const { translate } = useLocalization()
const { report, listFormCode, id } = useParams<{
report: string
listFormCode: string
id: string
}>()
2026-02-24 20:44:16 +00:00
const location = useLocation()
const token = useStoreState((state) => state.auth.session.token)
const themeMode = useStoreState((state) => state.theme.mode)
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
const isDarkMode = themeMode === MODE_DARK
2026-02-24 20:44:16 +00:00
useEffect(() => {
loadViewerCss(isDarkMode)
}, [isDarkMode])
2026-02-24 20:44:16 +00:00
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])
2026-02-24 20:44:16 +00:00
const reportUrlWithParams = useMemo(() => {
return createReportUrl({ report, id, listFormCode }, location.search)
}, [report, id, listFormCode, location.search])
2026-02-24 20:44:16 +00:00
if (!reportUrlWithParams) return null
2026-02-24 20:44:16 +00:00
return (
<Container className={getViewerClassName(isDarkMode)}>
2026-02-24 20:44:16 +00:00
<Helmet
titleTemplate={`%s | ${APP_NAME}`}
title={translate('::App.Reports')}
defaultTitle={APP_NAME}
/>
{isRequestAuthReady && (
2026-02-24 20:44:16 +00:00
<ReportViewer reportUrl={reportUrlWithParams}>
<RequestOptions
host={import.meta.env.VITE_API_URL}
invokeAction="/DXXRDV"
2026-02-24 20:44:16 +00:00
/>
</ReportViewer>
)}
2026-02-24 20:44:16 +00:00
</Container>
)
}
export default DevexpressReportViewer