Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert DataSize change for public-facing API #24682

Merged
merged 4 commits into from
Mar 6, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Revert UI change
shangm2 committed Mar 6, 2025
commit 046754246e8f7d0a4c30527de3f824a299fda8f6
8 changes: 4 additions & 4 deletions presto-ui/src/components/LivePlan.jsx
Original file line number Diff line number Diff line change
@@ -98,18 +98,18 @@ export class StageStatistics extends React.Component<StageStatisticsProps, Stage
{stage.state}
<hr/>
CPU: {stats.totalCpuTime}<br />
Buffered: {stats.bufferedDataSizeInBytes}<br />
Buffered: {stats.bufferedDataSize}<br />
{stats.fullyBlocked ?
<div style={{color: '#ff0000'}}>Blocked: {stats.totalBlockedTime} </div> :
<div>Blocked: {stats.totalBlockedTime} </div>
}
Memory: {stats.userMemoryReservationInBytes}
Memory: {stats.userMemoryReservation}
<br/>
Splits: {"Q:" + stats.queuedDrivers + ", R:" + stats.runningDrivers + ", F:" + stats.completedDrivers}
<br/>
Lifespans: {stats.completedLifespans + " / " + stats.totalLifespans}
<hr/>
Input: {stats.rawInputDataSizeInBytes + " / " + formatRows(stats.rawInputPositions)}
Input: {stats.rawInputDataSize + " / " + formatRows(stats.rawInputPositions)}
</div>
</div>
);
@@ -250,7 +250,7 @@ export class LivePlan extends React.Component<LivePlanProps, LivePlanState> {
class: "plan-edge",
style: "stroke-width: 4px",
arrowheadClass: "plan-arrowhead",
label: sourceStats.outputDataSizeInBytes + " / " + formatRows(sourceStats.outputPositions),
label: sourceStats.outputDataSize + " / " + formatRows(sourceStats.outputPositions),
labelStyle: "color: #fff; font-weight: bold; font-size: 24px;",
labelType: "html",
});
35 changes: 18 additions & 17 deletions presto-ui/src/components/QueryDetail.jsx
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ import {
getTaskIdSuffix,
getTaskNumber,
GLYPHICON_HIGHLIGHT,
parseDataSize,
parseDuration,
precisionRound
} from "../utils";
@@ -580,23 +581,23 @@ class StageSummary extends React.Component {
Cumulative
</td>
<td className="stage-table-stat-text">
{formatDataSizeBytes(stage.latestAttemptExecutionInfo.stats.cumulativeUserMemory / 1000)}
{formatDataSize(stage.latestAttemptExecutionInfo.stats.cumulativeUserMemory / 1000)}
</td>
</tr>
<tr>
<td className="stage-table-stat-title">
Cumulative Total
</td>
<td className="stage-table-stat-text">
{formatDataSizeBytes(stage.latestAttemptExecutionInfo.stats.cumulativeTotalMemory / 1000)}
{formatDataSize(stage.latestAttemptExecutionInfo.stats.cumulativeTotalMemory / 1000)}
</td>
</tr>
<tr>
<td className="stage-table-stat-title">
Current
</td>
<td className="stage-table-stat-text">
{formatDataSize(stage.latestAttemptExecutionInfo.stats.userMemoryReservationInBytes)}
{stage.latestAttemptExecutionInfo.stats.userMemoryReservation}
</td>
</tr>
<tr>
@@ -612,7 +613,7 @@ class StageSummary extends React.Component {
Peak
</td>
<td className="stage-table-stat-text">
{formatDataSize(stage.latestAttemptExecutionInfo.stats.peakUserMemoryReservationInBytes)}
{stage.latestAttemptExecutionInfo.stats.peakUserMemoryReservation}
</td>
</tr>
</tbody>
@@ -946,7 +947,7 @@ export class QueryDetail extends React.Component {
lastScheduledTime: parseDuration(query.queryStats.totalScheduledTime),
lastCpuTime: parseDuration(query.queryStats.totalCpuTime),
lastRowInput: query.queryStats.processedInputPositions,
lastByteInput: query.queryStats.processedInputDataSizeInBytes,
lastByteInput: parseDataSize(query.queryStats.processedInputDataSize),

initialized: true,
ended: query.finalQueryInfo,
@@ -969,13 +970,13 @@ export class QueryDetail extends React.Component {
const currentScheduledTimeRate = (parseDuration(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000);
const currentCpuTimeRate = (parseDuration(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000);
const currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh;
const currentByteInputRate = (query.queryStats.processedInputDataSizeInBytes - lastByteInput) / elapsedSecsSinceLastRefresh;
const currentByteInputRate = (parseDataSize(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh;
this.setState({
scheduledTimeRate: addToHistory(currentScheduledTimeRate, this.state.scheduledTimeRate),
cpuTimeRate: addToHistory(currentCpuTimeRate, this.state.cpuTimeRate),
rowInputRate: addToHistory(currentRowInputRate, this.state.rowInputRate),
byteInputRate: addToHistory(currentByteInputRate, this.state.byteInputRate),
reservedMemory: addToHistory(query.queryStats.userMemoryReservationInBytes, this.state.reservedMemory),
reservedMemory: addToHistory(parseDataSize(query.queryStats.userMemoryReservation), this.state.reservedMemory),
});
}
this.resetTimer();
@@ -1480,7 +1481,7 @@ export class QueryDetail extends React.Component {
Input Data
</td>
<td className="info-text">
{formatDataSize(query.queryStats.processedInputDataSizeInBytes)}
{query.queryStats.processedInputDataSize}
</td>
</tr>
<tr>
@@ -1496,7 +1497,7 @@ export class QueryDetail extends React.Component {
Raw Input Data
</td>
<td className="info-text">
{formatDataSize(query.queryStats.rawInputDataSizeInBytes)}
{query.queryStats.rawInputDataSize}
</td>
</tr>
<tr>
@@ -1518,23 +1519,23 @@ export class QueryDetail extends React.Component {
</span>
</td>
<td className="info-text">
{formatDataSize(query.queryStats.shuffledDataSizeInBytes)}
{query.queryStats.shuffledDataSize}
</td>
</tr>
<tr>
<td className="info-title">
Peak User Memory
</td>
<td className="info-text">
{formatDataSize(query.queryStats.peakUserMemoryReservationInBytes)}
{query.queryStats.peakUserMemoryReservation}
</td>
</tr>
<tr>
<td className="info-title">
Peak Total Memory
</td>
<td className="info-text">
{formatDataSize(query.queryStats.peakTotalMemoryReservationInBytes)}
{query.queryStats.peakTotalMemoryReservation}
</td>
</tr>
<tr>
@@ -1574,7 +1575,7 @@ export class QueryDetail extends React.Component {
Output Data
</td>
<td className="info-text">
{formatDataSize(query.queryStats.outputDataSizeInBytes)}
{query.queryStats.outputDataSize}
</td>
</tr>
<tr>
@@ -1590,24 +1591,24 @@ export class QueryDetail extends React.Component {
Written Output Logical Data Size
</td>
<td className="info-text">
{formatDataSize(query.queryStats.writtenOutputLogicalDataSizeInBytes)}
{query.queryStats.writtenOutputLogicalDataSize}
</td>
</tr>
<tr>
<td className="info-title">
Written Output Physical Data Size
</td>
<td className="info-text">
{formatDataSize(query.queryStats.writtenOutputPhysicalDataSizeInBytes)}
{query.queryStats.writtenOutputPhysicalDataSize}
</td>
</tr>
{query.queryStats.spilledDataSizeInBytes > 0 &&
{parseDataSize(query.queryStats.spilledDataSize) > 0 &&
<tr>
<td className="info-title">
Spilled Data
</td>
<td className="info-text">
{formatDataSize(query.queryStats.spilledDataSizeInBytes)}
{query.queryStats.spilledDataSize}
</td>
</tr>
}
56 changes: 28 additions & 28 deletions presto-ui/src/components/QueryOverview.jsx
Original file line number Diff line number Diff line change
@@ -113,8 +113,8 @@ type QueryStats = {
totalCpuTime: string;
cumulativeUserMemory: number;
cumulativeTotalMemory: number;
userMemoryReservationInBytes: number;
peakUserMemoryReservationInBytes: number;
userMemoryReservation: string;
peakUserMemoryReservation: string;
runtimeStats: RuntimeStats;
elapsedTime: string;
createTime: string;
@@ -124,18 +124,18 @@ type QueryStats = {
totalPlanningTime: string;
executionTime: string;
processedInputPositions: number;
processedInputDataSizeInBytes: number;
processedInputDataSize: string;
rawInputPositions: number;
rawInputDataSizeInBytes: number;
rawInputDataSize: string;
shuffledPositions: number;
shuffledDataSizeInBytes: number;
peakTotalMemoryReservationInBytes: number;
shuffledDataSize: string;
peakTotalMemoryReservation: string;
outputPositions: number;
outputDataSizeInBytes: number;
outputDataSize: string;
writtenOutputPositions: number;
writtenOutputLogicalDataSizeInBytes: number;
writtenOutputPhysicalDataSizeInBytes: number;
spilledDataSizeInBytes: number;
writtenOutputLogicalDataSize: string;
writtenOutputPhysicalDataSize: string;
spilledDataSize: string;
}

type FailureInfo = {
@@ -729,23 +729,23 @@ function StageSummary({ index, prestoStage }: { index: number, prestoStage: Outp
Cumulative
</td>
<td className="stage-table-stat-text">
{formatDataSizeBytes(prestoStage.latestAttemptExecutionInfo.stats.cumulativeUserMemory / 1000)}
{formatDataSize(prestoStage.latestAttemptExecutionInfo.stats.cumulativeUserMemory / 1000)}
</td>
</tr>
<tr>
<td className="stage-table-stat-title">
Cumulative Total
</td>
<td className="stage-table-stat-text">
{formatDataSizeBytes(prestoStage.latestAttemptExecutionInfo.stats.cumulativeTotalMemory / 1000)}
{formatDataSize(prestoStage.latestAttemptExecutionInfo.stats.cumulativeTotalMemory / 1000)}
</td>
</tr>
<tr>
<td className="stage-table-stat-title">
Current
</td>
<td className="stage-table-stat-text">
{formatDataSize(prestoStage.latestAttemptExecutionInfo.stats.userMemoryReservationInBytes)}
{prestoStage.latestAttemptExecutionInfo.stats.userMemoryReservation}
</td>
</tr>
<tr>
@@ -761,7 +761,7 @@ function StageSummary({ index, prestoStage }: { index: number, prestoStage: Outp
Peak
</td>
<td className="stage-table-stat-text">
{formatDataSize(prestoStage.latestAttemptExecutionInfo.stats.peakUserMemoryReservationInBytes)}
{prestoStage.latestAttemptExecutionInfo.stats.peakUserMemoryReservation}
</td>
</tr>
</tbody>
@@ -1438,7 +1438,7 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
Input Data
</td>
<td className="info-text">
{formatDataSize(data.queryStats.processedInputDataSizeInBytes)}
{data.queryStats.processedInputDataSize}
</td>
</tr>
<tr>
@@ -1454,7 +1454,7 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
Raw Input Data
</td>
<td className="info-text">
{formatDataSize(data.queryStats.rawInputDataSizeInBytes)}
{data.queryStats.rawInputDataSize}
</td>
</tr>
<tr>
@@ -1474,23 +1474,23 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
</span>
</td>
<td className="info-text">
{formatDataSize(data.queryStats.shuffledDataSizeInBytes)}
{data.queryStats.shuffledDataSize}
</td>
</tr>
<tr>
<td className="info-title">
Peak User Memory
</td>
<td className="info-text">
{formatDataSize(data.queryStats.peakUserMemoryReservationInBytes)}
{data.queryStats.peakUserMemoryReservation}
</td>
</tr>
<tr>
<td className="info-title">
Peak Total Memory
</td>
<td className="info-text">
{formatDataSize(data.queryStats.peakTotalMemoryReservationInBytes)}
{data.queryStats.peakTotalMemoryReservation}
</td>
</tr>
<tr>
@@ -1506,15 +1506,15 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
Cumulative User Memory
</td>
<td className="info-text">
{formatDataSizeBytes(data.queryStats.cumulativeUserMemory / 1000.0) + " seconds"}
{formatDataSize(data.queryStats.cumulativeUserMemory / 1000.0)}
</td>
</tr>
<tr>
<td className="info-title">
Cumulative Total
</td>
<td className="info-text">
{formatDataSizeBytes(data.queryStats.cumulativeTotalMemory / 1000.0) + " seconds"}
{formatDataSize(data.queryStats.cumulativeTotalMemory / 1000.0)}
</td>
</tr>
<tr>
@@ -1530,7 +1530,7 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
Output Data
</td>
<td className="info-text">
{formatDataSize(data.queryStats.outputDataSizeInBytes)}
{data.queryStats.outputDataSize}
</td>
</tr>
<tr>
@@ -1546,24 +1546,24 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
Written Output Logical Data Size
</td>
<td className="info-text">
{formatDataSize(data.queryStats.writtenOutputLogicalDataSizeInBytes)}
{data.queryStats.writtenOutputLogicalDataSize}
</td>
</tr>
<tr>
<td className="info-title">
Written Output Physical Data Size
</td>
<td className="info-text">
{formatDataSize(data.queryStats.writtenOutputPhysicalDataSizeInBytes)}
{data.queryStats.writtenOutputPhysicalDataSize}
</td>
</tr>
{(data.queryStats.spilledDataSizeInBytes || 0) > 0 &&
{(parseDataSize(data.queryStats.spilledDataSize) || 0) > 0 &&
<tr>
<td className="info-title">
Spilled Data
</td>
<td className="info-text">
{formatDataSize(data.queryStats.spilledDataSizeInBytes)}
{data.queryStats.spilledDataSize}
</td>
</tr>
}
@@ -1612,7 +1612,7 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
</tr>
<tr className="tr-noborder">
<td className="info-sparkline-text">
{formatDataSize((data.queryStats.processedInputDataSizeInBytes || 0) / elapsedTime)}
{formatDataSize((parseDataSize(data.queryStats.processedInputDataSize) || 0) / elapsedTime)}
</td>
</tr>
<tr>
@@ -1622,7 +1622,7 @@ export default function QueryOverview({ data, show }: { data: QueryData, show: b
</tr>
<tr className="tr-noborder">
<td className="info-sparkline-text">
{formatDataSize(data.queryStats.userMemoryReservationInBytes || 0)}
{formatDataSize(parseDataSize(data.queryStats.userMemoryReservation) || 0)}
</td>
</tr>
</tbody>
2 changes: 1 addition & 1 deletion presto-ui/src/components/QueryPlanView.jsx
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ export default function PlanView({show, data}) {
class: "plan-edge",
style: "stroke-width: 4px",
arrowheadClass: "plan-arrowhead",
label: sourceStats.outputDataSizeInBytes + " / " + formatRows(sourceStats.outputPositions),
label: sourceStats.outputDataSize + " / " + formatRows(sourceStats.outputPositions),
labelStyle: "color: #fff; font-weight: bold; font-size: 24px;",
labelType: "html",
});
288 changes: 147 additions & 141 deletions presto-ui/src/components/QueryStageView.jsx

Large diffs are not rendered by default.

73 changes: 41 additions & 32 deletions presto-ui/src/components/StageDetail.jsx
Original file line number Diff line number Diff line change
@@ -13,14 +13,25 @@
*/

import React from "react";
import {createRoot} from 'react-dom/client';
import ReactDOM from "react-dom";
import { createRoot } from 'react-dom/client';
import ReactDOMServer from "react-dom/server";
import * as dagreD3 from "dagre-d3-es";
import * as d3 from "d3";
import {clsx} from 'clsx';

import {formatCount, formatDataSize, formatDuration, getFirstParameter, getTaskNumber, isQueryEnded, parseDuration} from "../utils";
import {initializeGraph, initializeSvg} from "../d3utils";
import { clsx } from 'clsx';

import {
formatCount,
formatDataSize,
formatDuration,
getChildren,
getFirstParameter,
getTaskNumber,
isQueryEnded,
parseDataSize,
parseDuration
} from "../utils";
import { initializeGraph, initializeSvg } from "../d3utils";
import {QueryHeader} from "./QueryHeader";

function getTotalWallTime(operator) {
@@ -34,7 +45,7 @@ class OperatorSummary extends React.Component {
const totalWallTime = parseDuration(operator.addInputWall) + parseDuration(operator.getOutputWall) + parseDuration(operator.finishWall) + parseDuration(operator.blockedWall);

const rowInputRate = totalWallTime === 0 ? 0 : (1.0 * operator.inputPositions) / (totalWallTime / 1000.0);
const byteInputRate = totalWallTime === 0 ? 0 : (1.0 * operator.inputDataSizeInBytes) / (totalWallTime / 1000.0);
const byteInputRate = totalWallTime === 0 ? 0 : (1.0 * parseDataSize(operator.inputDataSize)) / (totalWallTime / 1000.0);

return (
<div className="header-data">
@@ -53,7 +64,7 @@ class OperatorSummary extends React.Component {
Output
</td>
<td>
{formatCount(operator.outputPositions) + " rows (" + operator.outputDataSizeInBytes + ")"}
{formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")"}
</td>
</tr>
<tr>
@@ -85,7 +96,7 @@ class OperatorSummary extends React.Component {
Input
</td>
<td>
{formatCount(operator.inputPositions) + " rows (" + operator.inputDataSizeInBytes + ")"}
{formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")"}
</td>
</tr>
</tbody>
@@ -107,19 +118,19 @@ const BAR_CHART_PROPERTIES = {
disableHiddenCheck: true,
};

function OperatorStatistic({id, name, operators, supplier, renderer}) {
function OperatorStatistic({ id, name, operators, supplier, renderer }) {

React.useEffect(() => {
const statistic = operators.map(supplier);
const numTasks = operators.length;

const tooltipValueLookups = {'offset': {}};
const tooltipValueLookups = { 'offset': {} };
for (let i = 0; i < numTasks; i++) {
tooltipValueLookups['offset'][i] = "" + i;
}

const stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, {barWidth: 800 / numTasks, tooltipValueLookups: tooltipValueLookups});
$('#operator-statics-' + id).sparkline(statistic, $.extend({}, stageBarChartProperties, {numberFormatter: renderer}));
const stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: 800 / numTasks, tooltipValueLookups: tooltipValueLookups });
$('#operator-statics-' + id).sparkline(statistic, $.extend({}, stageBarChartProperties, { numberFormatter: renderer }));

}, [operators, supplier, renderer]);

@@ -129,13 +140,13 @@ function OperatorStatistic({id, name, operators, supplier, renderer}) {
{name}
</div>
<div className="col-10">
<span className="bar-chart" id={`operator-statics-${id}`}/>
<span className="bar-chart" id={`operator-statics-${id}`} />
</div>
</div>
);
}

function OperatorDetail({index, operator, tasks}) {
function OperatorDetail({ index, operator, tasks }) {
const selectedStatistics = [
{
name: "Total Wall Time",
@@ -152,7 +163,7 @@ function OperatorDetail({index, operator, tasks}) {
{
name: "Input Data Size",
id: "inputDataSize",
supplier: operator => operator.inputDataSizeInBytes,
supplier: operator => parseDataSize(operator.inputDataSize),
renderer: formatDataSize
},
{
@@ -164,7 +175,7 @@ function OperatorDetail({index, operator, tasks}) {
{
name: "Output Data Size",
id: "outputDataSize",
supplier: operator => operator.outputDataSizeInBytes,
supplier: operator => parseDataSize(operator.outputDataSize),
renderer: formatDataSize
},
];
@@ -195,17 +206,17 @@ function OperatorDetail({index, operator, tasks}) {
const totalWallTime = getTotalWallTime(operator);

const rowInputRate = totalWallTime === 0 ? 0 : (1.0 * operator.inputPositions) / totalWallTime;
const byteInputRate = totalWallTime === 0 ? 0 : (1.0 * operator.inputDataSizeInBytes) / (totalWallTime / 1000.0);
const byteInputRate = totalWallTime === 0 ? 0 : (1.0 * parseDataSize(operator.inputDataSize)) / (totalWallTime / 1000.0);

const rowOutputRate = totalWallTime === 0 ? 0 : (1.0 * operator.outputPositions) / totalWallTime;
const byteOutputRate = totalWallTime === 0 ? 0 : (1.0 * operator.outputDataSizeInBytes) / (totalWallTime / 1000.0);
const byteOutputRate = totalWallTime === 0 ? 0 : (1.0 * parseDataSize(operator.outputDataSize)) / (totalWallTime / 1000.0);

return (
<div className="col-12 container mx-2">
<div className="modal-header">
<h3 className="modal-title fs-5">
<small>Pipeline {operator.pipelineId}</small>
<br/>
<br />
{operator.operatorType}
</h3>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
@@ -221,7 +232,7 @@ function OperatorDetail({index, operator, tasks}) {
Input
</td>
<td>
{formatCount(operator.inputPositions) + " rows (" + operator.inputDataSizeInBytes + ")"}
{formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")"}
</td>
</tr>
<tr>
@@ -237,7 +248,7 @@ function OperatorDetail({index, operator, tasks}) {
Output
</td>
<td>
{formatCount(operator.outputPositions) + " rows (" + operator.outputDataSizeInBytes + ")"}
{formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")"}
</td>
</tr>
<tr>
@@ -349,9 +360,8 @@ class StageOperatorGraph extends React.Component {
}
const container = document.getElementById('operator-detail');
const root = createRoot(container);
root.render(<OperatorDetail key={event} operator={operatorStageSummary} tasks={stage.latestAttemptExecutionInfo.tasks}/>);
}
else {
root.render(<OperatorDetail key={event} operator={operatorStageSummary} tasks={stage.latestAttemptExecutionInfo.tasks} />);
} else {
return;
}
}
@@ -363,7 +373,7 @@ class StageOperatorGraph extends React.Component {
pipelineOperators.set(operator.pipelineId, []);
}
pipelineOperators.get(operator.pipelineId).push(operator);
});
});

const result = new Map();
pipelineOperators.forEach((pipelineOperators, pipelineId) => {
@@ -484,14 +494,13 @@ export class StageDetail extends React.Component {
this.timeoutId = setTimeout(this.refreshLoop, 1000);
}
}

static getQueryURL(id) {
static getQueryURL(id) {
if (!id || typeof id !== 'string' || id.length === 0) {
return "/v1/query/undefined";
}
const sanitizedId = id.replace(/[^a-z0-9_]/gi, '');
return sanitizedId.length > 0 ? `/v1/query/${encodeURIComponent(sanitizedId)}` : "/v1/query/undefined";
}
}

refreshLoop() {
clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously
@@ -505,7 +514,7 @@ export class StageDetail extends React.Component {
}
}


$.get(StageDetail.getQueryURL(rawQueryId), query => {
this.setState({
initialized: true,
@@ -610,15 +619,15 @@ export class StageDetail extends React.Component {
<div className="stage-dropdown" role="group">
<div className="btn-group">
<button type="button" className="btn bg-white btn-secondary text-dark dropdown-toggle"
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">Select Stage<span className="caret"/>
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">Select Stage<span className="caret"/>
</button>
<ul className="dropdown-menu bg-white">
{
allStages.map(stageId => (
<li key={stageId}>
<a className={clsx('dropdown-item text-dark', stage.plan.id === stageId && 'selected')}
onClick={() => this.setState({selectedStageId: stageId})}>{stageId}</a>
onClick={() => this.setState({selectedStageId: stageId})}>{stageId}</a>
</li>
))
}