import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import type { FC } 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 { createReportQueryString, createReportUrl } from './reportRouteParams' const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' const DEFAULT_REPORT_ZOOM = 1 interface ReportPreviewModel { autoFitBy?: ZoomAutoBy originalZoom?: number zoom?: number } interface ReportPreviewContainer { reportPreview?: ReportPreviewModel ReportPreview?: ReportPreviewModel previewModel?: ReportPreviewContainer args?: ReportPreviewContainer sender?: ReportPreviewContainer & { GetReportPreview?: () => ReportPreviewModel } } 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 } } const DevexpressReportViewer: 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 cultureName = useStoreState((state) => state.locale.currentLang) const configCultureName = useStoreState( (state) => state.abpConfig.config?.localization.currentCulture.cultureName, ) const [configuredRequestKey, setConfiguredRequestKey] = useState() const previewModelRef = useRef() const isDarkMode = themeMode === MODE_DARK const reportCultureName = cultureName || configCultureName const requestConfigKey = `${token ?? ''}\u0000${reportCultureName ?? ''}` const getReportPreview = useCallback( (eventOrModel?: ReportPreviewContainer | ReportPreviewModel) => { const event = eventOrModel as ReportPreviewContainer | undefined const eventArgs = event?.args const sender = event?.sender const previewModel = event?.previewModel const candidates = [ eventArgs?.reportPreview, eventArgs?.ReportPreview, eventArgs?.previewModel?.reportPreview, event?.reportPreview, event?.ReportPreview, previewModel?.reportPreview, sender?.GetReportPreview?.(), sender?.previewModel?.reportPreview, eventOrModel as ReportPreviewModel | undefined, previewModelRef.current, ] return candidates.find( (candidate) => candidate && ('zoom' in candidate || 'originalZoom' in candidate || 'autoFitBy' in candidate), ) }, [], ) const setDefaultZoom = useCallback( (eventOrModel?: ReportPreviewContainer | ReportPreviewModel) => { 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(() => { const nextHeaders = { ...(ajaxSetup.ajaxSettings.headers ?? {}) } delete nextHeaders.Authorization delete nextHeaders['Accept-Language'] ajaxSetup.ajaxSettings.headers = { ...nextHeaders, ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(reportCultureName ? { 'Accept-Language': reportCultureName } : {}), } setConfiguredRequestKey(requestConfigKey) }, [token, reportCultureName, requestConfigKey]) const reportUrlWithParams = useMemo(() => { const searchParams = new URLSearchParams(location.search) if (reportCultureName) { searchParams.set('cultureName', reportCultureName) } const search = createReportQueryString(searchParams) return createReportUrl({ report, id, listFormCode }, search ? `?${search}` : '') }, [report, id, listFormCode, location.search, reportCultureName]) if (!reportUrlWithParams) return null return ( {configuredRequestKey === requestConfigKey && ( )} ) } export default DevexpressReportViewer