blob: 6cb9c644468f18db7b2cffea4edd06b630110ec2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Button,
ButtonGroup,
Menu,
MenuDivider,
MenuItem,
Popover,
Position,
} from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import type { JSX } from 'react';
import React, { useState } from 'react';
import type { Execution } from '../../../druid-models';
import type { FileFormat } from '../../../utils';
import {
copyAndAlert,
copyQueryResultsToClipboard,
downloadQueryResults,
FILE_FORMAT_TO_LABEL,
FILE_FORMATS,
formatDurationHybrid,
formatInteger,
oneOf,
pluralIfNeeded,
} from '../../../utils';
import { DestinationPagesDialog } from '../destination-pages-dialog/destination-pages-dialog';
import './execution-summary-panel.scss';
export interface ExecutionSummaryPanelProps {
execution: Execution | undefined;
queryErrorDuration: number | undefined;
onExecutionDetail(): void;
onReset?: () => void;
}
export const ExecutionSummaryPanel = React.memo(function ExecutionSummaryPanel(
props: ExecutionSummaryPanelProps,
) {
const { execution, queryErrorDuration, onExecutionDetail, onReset } = props;
const [showDestinationPages, setShowDestinationPages] = useState(false);
const queryResult = execution?.result;
const buttons: JSX.Element[] = [];
if (typeof queryErrorDuration === 'number') {
buttons.push(
<Button
key="timing"
minimal
text={`Error after ${formatDurationHybrid(queryErrorDuration)}`}
/>,
);
}
if (queryResult) {
const wrapQueryLimit = queryResult.getSqlOuterLimit();
let resultCount: string;
const numTotalRows = execution?.destination?.numTotalRows;
if (typeof wrapQueryLimit === 'undefined' && typeof numTotalRows === 'number') {
resultCount = pluralIfNeeded(numTotalRows, 'result');
} else {
const hasMoreResults = queryResult.getNumResults() === wrapQueryLimit;
resultCount = hasMoreResults
? `${formatInteger(queryResult.getNumResults() - 1)}+ results`
: pluralIfNeeded(queryResult.getNumResults(), 'result');
}
const warningCount = execution?.stages?.getWarningCount();
const handleDownload = (format: FileFormat) => {
downloadQueryResults(queryResult, `results-${execution.id}.${format}`, format);
};
const handleCopy = (format: FileFormat) => {
copyQueryResultsToClipboard(queryResult, format);
};
buttons.push(
<Button
key="results"
minimal
text={
resultCount +
(warningCount ? ` and ${pluralIfNeeded(warningCount, 'warning')}` : '') +
(execution.duration ? ` in ${formatDurationHybrid(execution.duration)}` : '')
}
onClick={() => {
if (!execution) return;
if (oneOf(execution.engine, 'sql-msq-task', 'sql-msq-dart')) {
onExecutionDetail();
} else {
copyAndAlert(execution.id, `Query ID (${execution.id}) copied to clipboard`);
}
}}
data-tooltip={
execution &&
(oneOf(execution.engine, 'sql-msq-task', 'sql-msq-dart')
? `Open details for\n${execution.id}`
: `Query ID\n${execution.id}\n(click to copy)`)
}
/>,
<Popover
key="download"
className="download-button"
position={Position.BOTTOM_RIGHT}
content={
<Menu>
{execution.destinationPages && (
<>
<MenuDivider title="Download in bulk" />
<MenuItem text="Show result pages" onClick={() => setShowDestinationPages(true)} />
<MenuDivider title="Download data in view" />
</>
)}
<MenuItem text="Download results as...">
{FILE_FORMATS.map(fileFormat => (
<MenuItem
key={fileFormat}
text={FILE_FORMAT_TO_LABEL[fileFormat]}
onClick={() => handleDownload(fileFormat)}
/>
))}
</MenuItem>
<MenuItem text="Copy to clipboard as...">
{FILE_FORMATS.map(fileFormat => (
<MenuItem
key={fileFormat}
text={FILE_FORMAT_TO_LABEL[fileFormat]}
onClick={() => handleCopy(fileFormat)}
/>
))}
</MenuItem>
</Menu>
}
>
<Button icon={IconNames.DOWNLOAD} data-tooltip="Download" minimal />
</Popover>,
);
}
if (onReset) {
buttons.push(
<Button
key="reset"
icon={IconNames.CROSS}
data-tooltip="Clear output"
minimal
onClick={onReset}
/>,
);
}
return (
<ButtonGroup className="execution-summary-panel" minimal>
{buttons}
{showDestinationPages && execution && (
<DestinationPagesDialog
execution={execution}
onClose={() => setShowDestinationPages(false)}
/>
)}
</ButtonGroup>
);
});