/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import React, { useEffect, useState } from 'react'; import { i18n } from '@osd/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageHeader, EuiTitle, EuiPageBody, EuiPageContent, EuiHorizontalRule, EuiSpacer, EuiDescriptionList, EuiDescriptionListTitle, EuiDescriptionListDescription, EuiPageHeaderSection, EuiLink, EuiIcon, EuiGlobalToastList, } from '@elastic/eui'; import { fileFormatsUpper, generateReportById } from '../main_utils'; import { GenerateReportLoadingModal } from '../loading_modal'; import { ReportSchemaType } from '../../../../server/model'; import dateMath from '@elastic/datemath'; import { permissionsMissingActions, permissionsMissingToast, timeRangeMatcher, } from '../../utils/utils'; import { TRIGGER_TYPE } from '../../../../server/routes/utils/constants'; interface ReportDetails { reportName: string; description: string; created: string; lastUpdated: string; source: string; time_period: string; defaultFileFormat: string; state: string | undefined; reportHeader: string; reportFooter: string; triggerType: string; scheduleType: string; scheduleDetails: string; queryUrl: string; } export const ReportDetailsComponent = (props: { reportDetailsComponentTitle: any; reportDetailsComponentContent: any; }) => { const { reportDetailsComponentTitle, reportDetailsComponentContent } = props; return ( {reportDetailsComponentTitle} {reportDetailsComponentContent} ); }; // convert markdown to plain text, trim it if it's longer than 3 lines export const trimAndRenderAsText = (markdown: string) => { if (!markdown) return markdown; const lines = markdown.split('\n').filter((line) => line); const elements = lines.slice(0, 3).map((line, i) =>

{line}

); return lines.length <= 3 ? elements : elements.concat(

...

); }; export const formatEmails = (emails: string[]) => { return Array.isArray(emails) ? emails.join(', ') : emails; }; export function ReportDetails(props: { match?: any; setBreadcrumbs?: any; httpClient: any; }) { const [reportDetails, setReportDetails] = useState({ reportName: '', description: '', created: '', lastUpdated: '', source: '', time_period: '', defaultFileFormat: '', state: '', reportHeader: '', reportFooter: '', triggerType: '', scheduleType: '', scheduleDetails: '', queryUrl: '' }); const [toasts, setToasts] = useState([]); const [showLoading, setShowLoading] = useState(false); const reportId = props.match['params']['reportId']; const handleLoading = (e: boolean | ((prevState: boolean) => boolean)) => { setShowLoading(e); }; const addPermissionsMissingDownloadToastHandler = () => { const toast = permissionsMissingToast( permissionsMissingActions.GENERATING_REPORT ); // @ts-ignore setToasts(toasts.concat(toast)); }; const handlePermissionsMissingDownloadToast = () => { addPermissionsMissingDownloadToastHandler(); }; const addErrorToastHandler = ( title = i18n.translate( 'opensearch.reports.details.errorLoadingReportDetails', { defaultMessage: 'Error loading report details.' } ), text = '' ) => { const errorToast = { title, text, color: 'danger', iconType: 'alert', id: 'reportDetailsErrorToast', }; // @ts-ignore setToasts(toasts.concat(errorToast)); }; const handleErrorToast = (title?: string, text?: string) => { addErrorToastHandler(title, text); }; const addSuccessToastHandler = () => { const successToast = { title: 'Success', color: 'success', text: (

{i18n.translate( 'opensearch.reports.details.reportSuccessfullyDownloaded', { defaultMessage: 'Report successfully downloaded!' } )}

), id: 'onDemandDownloadSuccessToast', }; // @ts-ignore setToasts(toasts.concat(successToast)); }; const handleSuccessToast = () => { addSuccessToastHandler(); }; const removeToast = (removedToast: { id: any; }) => { setToasts(toasts.filter((toast : any) => toast.id !== removedToast.id)); }; const handleReportDetails = (e: React.SetStateAction) => { setReportDetails(e); }; const convertTimestamp = (timestamp: number | undefined) => { let displayDate = `\u2014`; if (timestamp) { let readableDate = new Date(timestamp); displayDate = readableDate.toLocaleString(); } return displayDate; }; const parseTimePeriod = (queryUrl: string) => { let [fromDateString, toDateString] : RegExpMatchArray | null = queryUrl.match( timeRangeMatcher ); fromDateString = decodeURIComponent(fromDateString.replace(/[']+/g, '')); toDateString = decodeURIComponent(toDateString.replace(/[']+/g, '')); let fromDateParsed = dateMath.parse(fromDateString); let toDateParsed = dateMath.parse(toDateString, { roundUp: true }); const fromTimePeriod = fromDateParsed?.toDate(); const toTimePeriod = toDateParsed?.toDate(); return ( fromTimePeriod?.toLocaleString() + ' -> ' + toTimePeriod?.toLocaleString() ); }; const getReportDetailsData = ( report: ReportSchemaType ) : ReportDetails => { const { report_definition: reportDefinition, last_updated: lastUpdated, state, query_url: queryUrl, } = report; const { report_params: reportParams, trigger } = reportDefinition; const { trigger_type: triggerType, trigger_params: triggerParams, } = trigger; const coreParams = reportParams.core_params; // covert timestamp to local date-time string let reportDetails = { reportName: reportParams.report_name, description: reportParams.description === '' ? `\u2014` : reportParams.description, created: convertTimestamp(report.time_created), lastUpdated: convertTimestamp(report.last_updated), source: reportParams.report_source, // TODO: we have all data needed, time_from, time_to, time_duration, // think of a way to better display time_period: (reportParams.report_source !== 'Notebook') ? parseTimePeriod(queryUrl) : `\u2014`, defaultFileFormat: coreParams.report_format, state: state, reportHeader: reportParams.core_params.hasOwnProperty('header') && reportParams.core_params.header != '' ? reportParams.core_params.header : `\u2014`, reportFooter: reportParams.core_params.hasOwnProperty('footer') && reportParams.core_params.footer != '' ? reportParams.core_params.footer : `\u2014`, triggerType: triggerType, scheduleType: triggerParams ? triggerParams.schedule_type : `\u2014`, scheduleDetails: `\u2014`, queryUrl: queryUrl, }; return reportDetails; }; useEffect(() => { const { httpClient } = props; httpClient .get('../api/reporting/reports/' + reportId) .then((response: ReportSchemaType) => { handleReportDetails(getReportDetailsData(response)); props.setBreadcrumbs([ { text: i18n.translate( 'opensearch.reports.details.breadcrumb.reporting', { defaultMessage: 'Reporting' } ), href: '#', }, { text: i18n.translate( 'opensearch.reports.details.breadcrumb.reportDetails', { defaultMessage: 'Report details: {name}', values: { name: response.report_definition.report_params.report_name, }, } ), }, ]); }) .catch((error: any) => { console.log('Error when fetching report details: ', error); handleErrorToast(); }); }, []); const downloadIconDownload = async () => { handleLoading(true); await generateReportById( reportId, props.httpClient, handleSuccessToast, handleErrorToast, handlePermissionsMissingDownloadToast ); handleLoading(false); }; const fileFormatDownload = (data: ReportDetails) => { let formatUpper = data['defaultFileFormat']; formatUpper = fileFormatsUpper[formatUpper]; return ( {formatUpper + ' '} ); }; const sourceURL = (data: ReportDetails) => { return ( {data['source']} ); }; const triggerSection = reportDetails.triggerType === TRIGGER_TYPE.onDemand ? ( ) : ( ) const showLoadingModal = showLoading ? ( ) : null; return (

{i18n.translate('opensearch.reports.details.title', { defaultMessage: 'Report details', })}

{reportDetails.reportName}

{i18n.translate('opensearch.reports.details.reportSettings', { defaultMessage: 'Report Settings', })}

{triggerSection}
{showLoadingModal}
); }