This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch better_supervisor_view in repository https://gitbox.apache.org/repos/asf/druid.git
commit 78ad36ba85028d12372fe684e178593c00235d6a Author: Vadim Ogievetsky <[email protected]> AuthorDate: Fri Apr 19 12:52:06 2024 -0700 better tabs --- .../compaction-history-dialog.tsx | 4 +- .../kill-datasource-dialog.tsx | 7 +- .../supervisor-table-action-dialog.tsx | 14 +- .../task-table-action-dialog.tsx | 40 ++--- .../supervisor-status/supervisor-status.ts | 11 +- .../src/views/load-data-view/info-messages.tsx | 6 +- .../src/views/load-data-view/load-data-view.tsx | 10 +- .../src/views/segments-view/segments-view.tsx | 6 +- .../views/supervisors-view/supervisors-view.scss | 4 + .../views/supervisors-view/supervisors-view.tsx | 171 ++++++++++++++------- web-console/src/views/tasks-view/tasks-view.tsx | 6 +- 11 files changed, 179 insertions(+), 100 deletions(-) diff --git a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx index 4cdc916ee74..cb886d0483d 100644 --- a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx +++ b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Button, Callout, Classes, Code, Dialog, Tab, Tabs } from '@blueprintjs/core'; +import { Button, Callout, Classes, Dialog, Tab, Tabs, Tag } from '@blueprintjs/core'; import * as JSONBig from 'json-bigint-native'; import React, { useState } from 'react'; @@ -117,7 +117,7 @@ export const CompactionHistoryDialog = React.memo(function CompactionHistoryDial </Tabs> ) : ( <div> - There is no compaction history for <Code>{datasource}</Code>. + There is no compaction history for <Tag minimal>{datasource}</Tag>. </div> ) ) : historyState.loading ? ( diff --git a/web-console/src/dialogs/kill-datasource-dialog/kill-datasource-dialog.tsx b/web-console/src/dialogs/kill-datasource-dialog/kill-datasource-dialog.tsx index f95a5a5d3b8..dba85268d00 100644 --- a/web-console/src/dialogs/kill-datasource-dialog/kill-datasource-dialog.tsx +++ b/web-console/src/dialogs/kill-datasource-dialog/kill-datasource-dialog.tsx @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Code, Intent } from '@blueprintjs/core'; +import { Intent, Tag } from '@blueprintjs/core'; import React, { useState } from 'react'; import { FormGroupWithInfo, PopoverText } from '../../components'; @@ -74,13 +74,14 @@ export const KillDatasourceDialog = function KillDatasourceDialog( warningChecks={[ <> I understand that this operation will delete all metadata about the unused segments of{' '} - <Code>{datasource}</Code> and removes them from deep storage. + <Tag minimal>{datasource}</Tag> and removes them from deep storage. </>, 'I understand that this operation cannot be undone.', ]} > <p> - Are you sure you want to permanently delete unused segments in <Code>{datasource}</Code>? + Are you sure you want to permanently delete unused segments in{' '} + <Tag minimal>{datasource}</Tag>? </p> <p>This action is not reversible and the data deleted will be lost.</p> <FormGroupWithInfo diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx index 02d9e3c28b8..5e3d9e50028 100644 --- a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx +++ b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx @@ -28,6 +28,8 @@ import { TableActionDialog } from '../table-action-dialog/table-action-dialog'; import { SupervisorStatisticsTable } from './supervisor-statistics-table/supervisor-statistics-table'; +type SupervisorTableActionDialogTab = 'status' | 'stats' | 'spec' | 'history'; + interface SupervisorTableActionDialogProps { supervisorId: string; actions: BasicAction[]; @@ -38,7 +40,7 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc props: SupervisorTableActionDialogProps, ) { const { supervisorId, actions, onClose } = props; - const [activeTab, setActiveTab] = useState('status'); + const [activeTab, setActiveTab] = useState<SupervisorTableActionDialogTab>('status'); const supervisorTableSideButtonMetadata: SideButtonMetaData[] = [ { @@ -49,15 +51,15 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc }, { icon: 'chart', - text: 'Statistics', + text: 'Task stats', active: activeTab === 'stats', onClick: () => setActiveTab('stats'), }, { icon: 'align-left', - text: 'Payload', - active: activeTab === 'payload', - onClick: () => setActiveTab('payload'), + text: 'Spec', + active: activeTab === 'spec', + onClick: () => setActiveTab('spec'), }, { icon: 'history', @@ -88,7 +90,7 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc downloadFilename={`supervisor-stats-${supervisorId}.json`} /> )} - {activeTab === 'payload' && ( + {activeTab === 'spec' && ( <ShowJson endpoint={supervisorEndpointBase} transform={x => cleanSpec(x, true)} diff --git a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx index a0a5dbbf13f..9edc5d996f4 100644 --- a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx +++ b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx @@ -25,18 +25,20 @@ import type { BasicAction } from '../../utils/basic-action'; import type { SideButtonMetaData } from '../table-action-dialog/table-action-dialog'; import { TableActionDialog } from '../table-action-dialog/table-action-dialog'; +type TaskTableActionDialogTab = 'status' | 'report' | 'spec' | 'log'; + interface TaskTableActionDialogProps { taskId: string; actions: BasicAction[]; - onClose: () => void; status: string; + onClose(): void; } export const TaskTableActionDialog = React.memo(function TaskTableActionDialog( props: TaskTableActionDialogProps, ) { const { taskId, actions, onClose, status } = props; - const [activeTab, setActiveTab] = useState('status'); + const [activeTab, setActiveTab] = useState<TaskTableActionDialogTab>('status'); const taskTableSideButtonMetadata: SideButtonMetaData[] = [ { @@ -45,21 +47,21 @@ export const TaskTableActionDialog = React.memo(function TaskTableActionDialog( active: activeTab === 'status', onClick: () => setActiveTab('status'), }, - { - icon: 'align-left', - text: 'Payload', - active: activeTab === 'payload', - onClick: () => setActiveTab('payload'), - }, { icon: 'comparison', text: 'Reports', - active: activeTab === 'reports', - onClick: () => setActiveTab('reports'), + active: activeTab === 'report', + onClick: () => setActiveTab('report'), + }, + { + icon: 'align-left', + text: 'Spec', + active: activeTab === 'spec', + onClick: () => setActiveTab('spec'), }, { icon: 'align-justify', - text: 'Logs', + text: 'Log', active: activeTab === 'log', onClick: () => setActiveTab('log'), }, @@ -80,20 +82,20 @@ export const TaskTableActionDialog = React.memo(function TaskTableActionDialog( downloadFilename={`task-status-${taskId}.json`} /> )} - {activeTab === 'payload' && ( - <ShowJson - endpoint={taskEndpointBase} - transform={x => deepGet(x, 'payload') || x} - downloadFilename={`task-payload-${taskId}.json`} - /> - )} - {activeTab === 'reports' && ( + {activeTab === 'report' && ( <ShowJson endpoint={`${taskEndpointBase}/reports`} transform={x => deepGet(x, 'ingestionStatsAndErrors.payload') || x} downloadFilename={`task-reports-${taskId}.json`} /> )} + {activeTab === 'spec' && ( + <ShowJson + endpoint={taskEndpointBase} + transform={x => deepGet(x, 'payload') || x} + downloadFilename={`task-payload-${taskId}.json`} + /> + )} {activeTab === 'log' && ( <ShowLog tail={status === 'RUNNING'} diff --git a/web-console/src/druid-models/supervisor-status/supervisor-status.ts b/web-console/src/druid-models/supervisor-status/supervisor-status.ts index bdbc3187736..d76ad19cc14 100644 --- a/web-console/src/druid-models/supervisor-status/supervisor-status.ts +++ b/web-console/src/druid-models/supervisor-status/supervisor-status.ts @@ -42,20 +42,27 @@ export interface SupervisorStatus { healthy: boolean; state: string; detailedState: string; - recentErrors: any[]; + recentErrors: SupervisorError[]; }; } export interface SupervisorStatusTask { id: string; startingOffsets: SupervisorOffsetMap; - startTime: '2024-04-12T21:35:34.834Z'; + startTime: string; remainingSeconds: number; type: string; currentOffsets: SupervisorOffsetMap; lag: SupervisorOffsetMap; } +export interface SupervisorError { + timestamp: string; + exceptionClass: string; + message: string; + streamException: boolean; +} + export type SupervisorStats = Record<string, Record<string, RowStats>>; export type RowStatsKey = 'totals' | '1m' | '5m' | '15m'; diff --git a/web-console/src/views/load-data-view/info-messages.tsx b/web-console/src/views/load-data-view/info-messages.tsx index b88cf8a70c2..ad9e96667db 100644 --- a/web-console/src/views/load-data-view/info-messages.tsx +++ b/web-console/src/views/load-data-view/info-messages.tsx @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Button, Callout, Code, FormGroup, Intent } from '@blueprintjs/core'; +import { Button, Callout, Code, FormGroup, Intent, Tag } from '@blueprintjs/core'; import React from 'react'; import { ExternalLink, LearnMore } from '../../components'; @@ -236,8 +236,8 @@ export const AppendToExistingIssue = React.memo(function AppendToExistingIssue( <FormGroup> <Callout intent={Intent.DANGER}> <p> - Only <Code>dynamic</Code> partitioning supports <Code>appendToExisting: true</Code>. You - have currently selected <Code>{partitionsSpecType}</Code> partitioning. + Only <Tag minimal>dynamic</Tag> partitioning supports <Code>appendToExisting: true</Code>. + You have currently selected <Tag minimal>{partitionsSpecType}</Tag> partitioning. </p> <Button intent={Intent.SUCCESS} diff --git a/web-console/src/views/load-data-view/load-data-view.tsx b/web-console/src/views/load-data-view/load-data-view.tsx index fdb45420e0a..d95943b4f1c 100644 --- a/web-console/src/views/load-data-view/load-data-view.tsx +++ b/web-console/src/views/load-data-view/load-data-view.tsx @@ -34,6 +34,7 @@ import { Radio, RadioGroup, Switch, + Tag, TextArea, } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; @@ -3073,8 +3074,9 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat <p>Your partitioning and sorting configuration is uncommon.</p> <p> For best performance the first dimension in your schema ( - <Code>{firstDimensionName}</Code>), which is what the data will be primarily sorted - on, commonly matches the partitioning dimension (<Code>{partitionDimension}</Code>). + <Tag minimal>{firstDimensionName}</Tag>), which is what the data will be primarily + sorted on, commonly matches the partitioning dimension ( + <Tag minimal>{partitionDimension}</Tag>). </p> <p> <Button @@ -3452,11 +3454,11 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat <p> You have enabled type-aware schema discovery ( <Code>useSchemaDiscovery: true</Code>) to ingest data into the existing - datasource <Code>{datasource}</Code>. + datasource <Tag minimal>{datasource}</Tag>. </p> <p> If you used string-based schema discovery when first ingesting data to{' '} - <Code>{datasource}</Code>, using type-aware schema discovery now can cause + <Tag minimal>{datasource}</Tag>, using type-aware schema discovery now can cause problems with the values multi-value string dimensions. </p> <p> diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx index 44d59f6fa95..ba4b84b76c4 100644 --- a/web-console/src/views/segments-view/segments-view.tsx +++ b/web-console/src/views/segments-view/segments-view.tsx @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Button, ButtonGroup, Code, Intent, Label, MenuItem, Switch } from '@blueprintjs/core'; +import { Button, ButtonGroup, Intent, Label, MenuItem, Switch, Tag } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { C, L, SqlComparison, SqlExpression } from '@druid-toolkit/query'; import classNames from 'classnames'; @@ -919,7 +919,7 @@ END AS "time_span"`, ); return resp.data; }} - confirmButtonText="Drop Segment" + confirmButtonText="Drop segment" successText="Segment drop request acknowledged, next time the coordinator runs segment will be dropped" failText="Could not drop segment" intent={Intent.DANGER} @@ -931,7 +931,7 @@ END AS "time_span"`, }} > <p> - Are you sure you want to drop segment <Code>{terminateSegmentId}</Code>? + Are you sure you want to drop segment <Tag minimal>{terminateSegmentId}</Tag>? </p> <p>This action is not reversible.</p> </AsyncActionDialog> diff --git a/web-console/src/views/supervisors-view/supervisors-view.scss b/web-console/src/views/supervisors-view/supervisors-view.scss index edf04bc4e0d..6db2b646d95 100644 --- a/web-console/src/views/supervisors-view/supervisors-view.scss +++ b/web-console/src/views/supervisors-view/supervisors-view.scss @@ -28,5 +28,9 @@ top: $view-control-bar-height + $standard-padding; bottom: 0; width: 100%; + + .title-button { + cursor: pointer; + } } } diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx b/web-console/src/views/supervisors-view/supervisors-view.tsx index 6e872792220..0483cbd2184 100644 --- a/web-console/src/views/supervisors-view/supervisors-view.tsx +++ b/web-console/src/views/supervisors-view/supervisors-view.tsx @@ -16,9 +16,11 @@ * limitations under the License. */ -import { Button, Code, Intent, Menu, MenuItem, Position } from '@blueprintjs/core'; +import { Icon, Intent, Menu, MenuItem, Position, Tag } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Popover2 } from '@blueprintjs/popover2'; +import * as JSONBig from 'json-bigint-native'; +import type { JSX } from 'react'; import React from 'react'; import type { Filter } from 'react-table'; import ReactTable from 'react-table'; @@ -42,7 +44,12 @@ import { SupervisorTableActionDialog, } from '../../dialogs'; import { SupervisorResetOffsetsDialog } from '../../dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog'; -import type { QueryWithContext, RowStatsKey, SupervisorStatus } from '../../druid-models'; +import type { + IngestionSpec, + QueryWithContext, + RowStatsKey, + SupervisorStatus, +} from '../../druid-models'; import { getTotalSupervisorStats } from '../../druid-models'; import type { Capabilities } from '../../helpers'; import { @@ -64,6 +71,7 @@ import { hasPopoverOpen, LocalStorageBackedVisibility, LocalStorageKeys, + nonEmptyArray, oneOf, pluralIfNeeded, queryDruidSql, @@ -81,16 +89,16 @@ const supervisorTableColumns: string[] = [ 'Type', 'Topic/Stream', 'Status', - 'Active tasks', + 'Configured tasks', + 'Running tasks', 'Aggregate lag', 'Stats', ACTION_COLUMN_LABEL, ]; -const ROW_STATS_KEYS: RowStatsKey[] = ['totals', '1m', '5m', '15m']; +const ROW_STATS_KEYS: RowStatsKey[] = ['1m', '5m', '15m']; function getRowStatsKeyTitle(key: RowStatsKey) { - if (key === 'totals') return 'Total'; return `Rate over past ${pluralIfNeeded(parseInt(key, 10), 'minute')}`; } @@ -104,6 +112,7 @@ interface SupervisorQueryResultRow { type: string; source: string; detailed_state: string; + spec?: IngestionSpec; suspended: boolean; status?: SupervisorStatus; stats?: any; @@ -182,14 +191,6 @@ export class SupervisorsView extends React.PureComponent< SupervisorQueryResultRow[] >; - static SUPERVISOR_SQL_BASE = `WITH s AS (SELECT - "supervisor_id", - "type", - "source", - CASE WHEN "suspended" = 0 THEN "detailed_state" ELSE 'SUSPENDED' END AS "detailed_state", - "suspended" = 1 AS "suspended" -FROM "sys"."supervisors")`; - constructor(props: SupervisorsViewProps) { super(props); @@ -219,7 +220,14 @@ FROM "sys"."supervisors")`; let supervisors: SupervisorQueryResultRow[]; if (capabilities.hasSql()) { const sqlQuery = assemble( - SupervisorsView.SUPERVISOR_SQL_BASE, + 'WITH s AS (SELECT', + ' "supervisor_id",', + ' "type",', + ' "source",', + ` CASE WHEN "suspended" = 0 THEN "detailed_state" ELSE 'SUSPENDED' END AS "detailed_state",`, + visibleColumns.shown('Configured tasks') ? ' "spec",' : undefined, + ' "suspended" = 1 AS "suspended"', + 'FROM "sys"."supervisors")', 'SELECT *', 'FROM s', filtered.length @@ -236,6 +244,13 @@ FROM "sys"."supervisors")`; }, cancelToken, ); + + for (const supervisor of supervisors) { + const spec: any = supervisor.spec; + if (typeof spec === 'string') { + supervisor.spec = JSONBig.parse(spec); + } + } } else if (capabilities.hasOverlordAccess()) { const supervisorList = ( await Api.instance.get('/druid/indexer/v1/supervisor?full', { cancelToken }) @@ -253,6 +268,7 @@ FROM "sys"."supervisors")`; 'n/a', state: deepGet(sup, 'state'), detailed_state: deepGet(sup, 'detailedState'), + spec: sup.spec, suspended: Boolean(deepGet(sup, 'suspended')), }; }); @@ -272,7 +288,7 @@ FROM "sys"."supervisors")`; } if (capabilities.hasOverlordAccess()) { - if (visibleColumns.shown('Active tasks') || visibleColumns.shown('Aggregate lag')) { + if (visibleColumns.shown('Running tasks') || visibleColumns.shown('Aggregate lag')) { try { for (const supervisor of supervisors) { cancelToken.throwIfRequested(); @@ -451,7 +467,7 @@ FROM "sys"."supervisors")`; }} > <p> - Are you sure you want to resume supervisor <Code>{resumeSupervisorId}</Code>? + Are you sure you want to resume supervisor <Tag minimal>{resumeSupervisorId}</Tag>? </p> </AsyncActionDialog> ); @@ -482,7 +498,7 @@ FROM "sys"."supervisors")`; }} > <p> - Are you sure you want to suspend supervisor <Code>{suspendSupervisorId}</Code>? + Are you sure you want to suspend supervisor <Tag minimal>{suspendSupervisorId}</Tag>? </p> </AsyncActionDialog> ); @@ -527,17 +543,20 @@ FROM "sys"."supervisors")`; this.supervisorQueryManager.rerunLastQuery(); }} warningChecks={[ - `I understand that resetting ${resetSupervisorId} will clear checkpoints and therefore lead to data loss or duplication.`, + <> + I understand that resetting <Tag minimal>{resetSupervisorId}</Tag> will clear + checkpoints and may lead to data loss or duplication. + </>, 'I understand that this operation cannot be undone.', ]} > <p> - Are you sure you want to hard reset supervisor <Code>{resetSupervisorId}</Code>? + Are you sure you want to hard reset supervisor <Tag minimal>{resetSupervisorId}</Tag>? </p> - <p>Hard resetting a supervisor will lead to data loss or data duplication.</p> + <p>Hard resetting a supervisor may lead to data loss or data duplication.</p> <p> - The reason for using this operation is to recover from a state in which the supervisor - ceases operating due to missing offsets. + Use this operation to restore functionality when the supervisor stops operating due to + missing offsets. </p> </AsyncActionDialog> ); @@ -568,7 +587,7 @@ FROM "sys"."supervisors")`; }} > <p> - Are you sure you want to terminate supervisor <Code>{terminateSupervisorId}</Code>? + Are you sure you want to terminate supervisor <Tag minimal>{terminateSupervisorId}</Tag>? </p> <p>This action is not reversible.</p> </AsyncActionDialog> @@ -673,29 +692,69 @@ FROM "sys"."supervisors")`; show: visibleColumns.shown('Status'), }, { - Header: 'Active tasks', - id: 'active_tasks', + Header: 'Configured tasks', + id: 'configured_tasks', width: 150, - accessor: 'status.payload.activeTasks', + accessor: 'spec', + filterable: false, + sortable: false, + className: 'padded', + Cell: ({ value }) => { + if (!value) return null; + const taskCount = deepGet(value, 'spec.ioConfig.taskCount'); + const replicas = deepGet(value, 'spec.ioConfig.replicas'); + if (typeof taskCount !== 'number' || typeof replicas !== 'number') return null; + return ( + <div> + <div>{formatInteger(taskCount * replicas)}</div> + <div> + {replicas === 1 + ? '(no replication)' + : `(${pluralIfNeeded(taskCount, 'task')} × ${pluralIfNeeded( + replicas, + 'replica', + )})`} + </div> + </div> + ); + }, + show: visibleColumns.shown('Configured tasks'), + }, + { + Header: 'Running tasks', + id: 'running_tasks', + width: 150, + accessor: 'status.payload', filterable: false, sortable: false, Cell: ({ value, original }) => { if (original.suspended) return; + let label: string | JSX.Element; + const { activeTasks, publishingTasks } = value || {}; + if (Array.isArray(activeTasks)) { + label = pluralIfNeeded(activeTasks.length, 'active task'); + if (nonEmptyArray(publishingTasks)) { + label = ( + <> + <div>{label}</div> + <div>{pluralIfNeeded(publishingTasks.length, 'publishing task')}</div> + </> + ); + } + } else { + label = 'n/a'; + } return ( <TableClickableCell onClick={() => goToTasks(original.supervisor_id, `index_${original.type}`)} hoverIcon={IconNames.ARROW_TOP_RIGHT} title="Go to tasks" > - {typeof value === 'undefined' - ? 'n/a' - : value.length > 0 - ? pluralIfNeeded(value.length, 'running task') - : `No running tasks`} + {label} </TableClickableCell> ); }, - show: visibleColumns.shown('Active tasks'), + show: visibleColumns.shown('Running tasks'), }, { Header: 'Aggregate lag', @@ -708,7 +767,30 @@ FROM "sys"."supervisors")`; Cell: ({ value }) => formatInteger(value), }, { - Header: twoLines('Stats', <i>{getRowStatsKeyTitle(statsKey)}</i>), + Header: twoLines( + 'Stats', + <Popover2 + position={Position.BOTTOM} + content={ + <Menu> + {ROW_STATS_KEYS.map(k => ( + <MenuItem + key={k} + icon={checkedCircleIcon(k === statsKey)} + text={getRowStatsKeyTitle(k)} + onClick={() => { + this.setState({ statsKey: k }); + }} + /> + ))} + </Menu> + } + > + <i className="title-button"> + {getRowStatsKeyTitle(statsKey)} <Icon icon={IconNames.CARET_DOWN} /> + </i> + </Popover2>, + ), id: 'stats', width: 220, filterable: false, @@ -889,7 +971,6 @@ FROM "sys"."supervisors")`; supervisorTableActionDialogId, supervisorTableActionDialogActions, visibleColumns, - statsKey, } = this.state; return ( @@ -902,28 +983,6 @@ FROM "sys"."supervisors")`; this.supervisorQueryManager.rerunLastQuery(auto); }} /> - <Popover2 - position={Position.BOTTOM} - content={ - <Menu> - {ROW_STATS_KEYS.map(k => ( - <MenuItem - key={k} - icon={checkedCircleIcon(k === statsKey)} - text={getRowStatsKeyTitle(k)} - onClick={() => { - this.setState({ statsKey: k }); - }} - /> - ))} - </Menu> - } - > - <Button - text={`Stats: ${getRowStatsKeyTitle(statsKey)}`} - rightIcon={IconNames.CARET_DOWN} - /> - </Popover2> {this.renderBulkSupervisorActions()} <TableColumnSelector columns={supervisorTableColumns} diff --git a/web-console/src/views/tasks-view/tasks-view.tsx b/web-console/src/views/tasks-view/tasks-view.tsx index 795c9908412..2be3575b9e1 100644 --- a/web-console/src/views/tasks-view/tasks-view.tsx +++ b/web-console/src/views/tasks-view/tasks-view.tsx @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core'; +import { Button, ButtonGroup, Intent, Label, MenuItem, Tag } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import React from 'react'; import type { Filter } from 'react-table'; @@ -310,7 +310,9 @@ ORDER BY this.taskQueryManager.rerunLastQuery(); }} > - <p>{`Are you sure you want to kill task '${killTaskId}'?`}</p> + <p> + Are you sure you want to kill task <Tag minimal>{killTaskId}</Tag>? + </p> </AsyncActionDialog> ); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
