import React, { useMemo, useEffect, useState, useCallback, useRef } from 'react' 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, { Callbacks, RequestOptions, } from 'devexpress-reporting-react/dx-report-viewer' import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native' import { ZoomAutoBy } from 'devexpress-reporting/viewer/constants' import { APP_NAME } from '@/constants/app.constant' import { MODE_DARK } from '@/constants/theme.constant' import { useStoreState } from '@/store' import { createReportUrl } from './reportRouteParams' const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides' const DEFAULT_REPORT_ZOOM = 1 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) => { 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) } } const DevexpressReportViewer: React.FC = () => { const { translate } = useLocalization() const { report, listFormCode, id } = useParams<{ report: string listFormCode: string id: string }>() const location = useLocation() const token = useStoreState((state) => state.auth.session.token) const themeMode = useStoreState((state) => state.theme.mode) const [isRequestAuthReady, setIsRequestAuthReady] = useState(false) const previewModelRef = useRef(null) const isDarkMode = themeMode === MODE_DARK const getReportPreview = useCallback((eventOrModel?: any) => { const eventArgs = eventOrModel?.args const sender = eventOrModel?.sender const candidates = [ eventArgs?.reportPreview, eventArgs?.ReportPreview, eventArgs?.previewModel?.reportPreview, eventOrModel?.reportPreview, eventOrModel?.ReportPreview, eventOrModel?.previewModel?.reportPreview, sender?.GetReportPreview?.(), sender?.previewModel?.reportPreview, previewModelRef.current, ] return candidates.find( (candidate) => candidate && ('zoom' in candidate || 'originalZoom' in candidate || 'autoFitBy' in candidate), ) }, []) const setDefaultZoom = useCallback((eventOrModel?: any) => { const preview = getReportPreview(eventOrModel) if (!preview) return previewModelRef.current = preview preview.autoFitBy = ZoomAutoBy.None preview.originalZoom = DEFAULT_REPORT_ZOOM preview.zoom = DEFAULT_REPORT_ZOOM }, [getReportPreview]) useEffect(() => { loadViewerCss(isDarkMode) }, [isDarkMode]) 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]) const reportUrlWithParams = useMemo(() => { return createReportUrl({ report, id, listFormCode }, location.search) }, [report, id, listFormCode, location.search]) if (!reportUrlWithParams) return null return ( {isRequestAuthReady && ( )} ) } export default DevexpressReportViewer