Skip to content

Commit c16f024

Browse files
authored
Merge branch 'main' into astandrik.vertical-progress-bar-1190
2 parents ea5aa01 + 3287f99 commit c16f024

File tree

19 files changed

+300
-76
lines changed

19 files changed

+300
-76
lines changed

src/components/CriticalActionDialog/CriticalActionDialog.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33
import {CircleXmarkFill, TriangleExclamationFill} from '@gravity-ui/icons';
44
import {Checkbox, Dialog, Icon} from '@gravity-ui/uikit';
55

6+
import {ResultIssues} from '../../containers/Tenant/Query/Issues/Issues';
67
import type {IResponseError} from '../../types/api/error';
78
import {cn} from '../../utils/cn';
89

@@ -13,6 +14,9 @@ import './CriticalActionDialog.scss';
1314
const b = cn('ydb-critical-dialog');
1415

1516
const parseError = (error: IResponseError) => {
17+
if (error.data && 'issues' in error.data && error.data.issues) {
18+
return <ResultIssues hideSeverity data={error.data} />;
19+
}
1620
if (error.status === 403) {
1721
return criticalActionDialogKeyset('no-rights-error');
1822
}

src/components/TooltipsContent/NodeEndpointsTooltipContent/NodeEndpointsTooltipContent.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,28 @@ interface NodeEdpointsTooltipProps {
2121
export const NodeEndpointsTooltipContent = ({data, nodeHref}: NodeEdpointsTooltipProps) => {
2222
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
2323
const info: (DefinitionListItemProps & {key: string})[] = [];
24+
if (data?.Host) {
25+
info.push({
26+
name: i18n('field_host'),
27+
children: data.Host,
28+
copyText: data.Host,
29+
key: 'Host',
30+
});
31+
}
32+
if (data?.Tenants?.[0]) {
33+
info.push({
34+
name: i18n('field_database'),
35+
children: data.Tenants[0],
36+
key: 'Database',
37+
});
38+
}
39+
if (data?.Roles?.length) {
40+
info.push({
41+
name: i18n('field_roles'),
42+
children: data.Roles.join(', '),
43+
key: 'Roles',
44+
});
45+
}
2446

2547
if (data?.Rack) {
2648
info.push({name: i18n('field_rack'), children: data.Rack, key: 'Rack'});
@@ -33,14 +55,6 @@ export const NodeEndpointsTooltipContent = ({data, nodeHref}: NodeEdpointsToolti
3355
}
3456
});
3557
}
36-
if (data?.Host) {
37-
info.push({
38-
name: i18n('field_host'),
39-
children: data.Host,
40-
copyText: data.Host,
41-
key: 'Host',
42-
});
43-
}
4458

4559
if (isUserAllowedToMakeChanges && nodeHref) {
4660
info.push({
@@ -56,7 +70,7 @@ export const NodeEndpointsTooltipContent = ({data, nodeHref}: NodeEdpointsToolti
5670
{info.map(({children, key, ...rest}) => {
5771
return (
5872
<DefinitionList.Item key={key} {...rest}>
59-
<span className={b('definition')}>{children}</span>
73+
<div className={b('definition')}>{children}</div>
6074
</DefinitionList.Item>
6175
);
6276
})}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"field_rack": "Rack",
33
"field_host": "Host",
4-
"context_developer-ui": "Developer UI"
4+
"context_developer-ui": "Developer UI",
5+
"field_database": "Database",
6+
"field_roles": "Roles"
57
}

src/containers/Nodes/Nodes.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,19 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =
113113
/>
114114
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
115115
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
116-
<EntitiesCount
117-
total={totalNodes}
118-
current={nodes.length}
119-
label={'Nodes'}
120-
loading={isLoading}
121-
/>
122116
<TableColumnSetup
123117
popupWidth={200}
124118
items={columnsToSelect}
125119
showStatus
126120
onUpdate={setColumns}
127121
sortable={false}
128122
/>
123+
<EntitiesCount
124+
total={totalNodes}
125+
current={nodes.length}
126+
label={'Nodes'}
127+
loading={isLoading}
128+
/>
129129
</React.Fragment>
130130
);
131131
};

src/containers/Nodes/PaginatedNodes.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,19 @@ export const PaginatedNodes = ({path, database, parentRef, additionalNodesProps}
9696
/>
9797
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
9898
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
99-
<EntitiesCount
100-
total={totalEntities}
101-
current={foundEntities}
102-
label={'Nodes'}
103-
loading={!inited}
104-
/>
10599
<TableColumnSetup
106100
popupWidth={200}
107101
items={columnsToSelect}
108102
showStatus
109103
onUpdate={setColumns}
110104
sortable={false}
111105
/>
106+
<EntitiesCount
107+
total={totalEntities}
108+
current={foundEntities}
109+
label={'Nodes'}
110+
loading={!inited}
111+
/>
112112
</React.Fragment>
113113
);
114114
};

src/containers/Operations/Operations.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {isAccessError} from '../../components/Errors/PageError/PageError';
55
import {ResponseError} from '../../components/Errors/ResponseError';
66
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
77
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
8-
import {operationListApi} from '../../store/reducers/operationList';
8+
import {operationsApi} from '../../store/reducers/operations';
99
import {useAutoRefreshInterval} from '../../utils/hooks';
1010

1111
import {OperationsControls} from './OperationsControls';
@@ -24,7 +24,7 @@ export function Operations({database}: OperationsProps) {
2424
const {kind, searchValue, pageSize, pageToken, handleKindChange, handleSearchChange} =
2525
useOperationsQueryParams();
2626

27-
const {data, isFetching, error} = operationListApi.useGetOperationListQuery(
27+
const {data, isFetching, error, refetch} = operationsApi.useGetOperationListQuery(
2828
{database, kind, page_size: pageSize, page_token: pageToken},
2929
{
3030
pollingInterval: autoRefreshInterval,
@@ -61,7 +61,7 @@ export function Operations({database}: OperationsProps) {
6161
<TableWithControlsLayout.Table loading={isFetching} className={b('table')}>
6262
{data ? (
6363
<ResizeableDataTable
64-
columns={getColumns()}
64+
columns={getColumns({database, refreshTable: refetch})}
6565
data={filteredOperations}
6666
emptyDataMessage={i18n('title_empty')}
6767
/>

src/containers/Operations/OperationsControls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {Select} from '@gravity-ui/uikit';
44

55
import {EntitiesCount} from '../../components/EntitiesCount';
66
import {Search} from '../../components/Search';
7-
import type {OperationKind} from '../../types/api/operationList';
7+
import type {OperationKind} from '../../types/api/operations';
88

99
import {OPERATION_KINDS} from './constants';
1010
import i18n from './i18n';

src/containers/Operations/columns.tsx

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
import {duration} from '@gravity-ui/date-utils';
2+
import {Ban, CircleStop} from '@gravity-ui/icons';
23
import type {Column as DataTableColumn} from '@gravity-ui/react-data-table';
3-
import {Text} from '@gravity-ui/uikit';
4+
import {ActionTooltip, Flex, Icon, Text} from '@gravity-ui/uikit';
45

6+
import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
57
import {CellWithPopover} from '../../components/CellWithPopover/CellWithPopover';
6-
import type {TOperation} from '../../types/api/operationList';
7-
import {EStatusCode} from '../../types/api/operationList';
8+
import {operationsApi} from '../../store/reducers/operations';
9+
import type {TOperation} from '../../types/api/operations';
10+
import {EStatusCode} from '../../types/api/operations';
811
import {EMPTY_DATA_PLACEHOLDER, HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants';
12+
import createToast from '../../utils/createToast';
913
import {formatDateTime} from '../../utils/dataFormatters/dataFormatters';
1014
import {parseProtobufTimestampToMs} from '../../utils/timeParsers';
1115

1216
import {COLUMNS_NAMES, COLUMNS_TITLES} from './constants';
1317
import i18n from './i18n';
1418

15-
export function getColumns(): DataTableColumn<TOperation>[] {
19+
import './Operations.scss';
20+
21+
export function getColumns({
22+
database,
23+
refreshTable,
24+
}: {
25+
database: string;
26+
refreshTable: VoidFunction;
27+
}): DataTableColumn<TOperation>[] {
1628
return [
1729
{
1830
name: COLUMNS_NAMES.ID,
@@ -114,5 +126,91 @@ export function getColumns(): DataTableColumn<TOperation>[] {
114126
return Date.now() - createTime;
115127
},
116128
},
129+
{
130+
name: 'Actions',
131+
sortable: false,
132+
resizeable: false,
133+
header: '',
134+
render: ({row}) => {
135+
return (
136+
<OperationsActions
137+
operation={row}
138+
database={database}
139+
refreshTable={refreshTable}
140+
/>
141+
);
142+
},
143+
},
117144
];
118145
}
146+
147+
interface OperationsActionsProps {
148+
operation: TOperation;
149+
database: string;
150+
refreshTable: VoidFunction;
151+
}
152+
153+
function OperationsActions({operation, database, refreshTable}: OperationsActionsProps) {
154+
const [cancelOperation, {isLoading: isLoadingCancel}] =
155+
operationsApi.useCancelOperationMutation();
156+
const [forgetOperation, {isLoading: isForgetLoading}] =
157+
operationsApi.useForgetOperationMutation();
158+
159+
const id = operation.id;
160+
if (!id) {
161+
return null;
162+
}
163+
164+
return (
165+
<Flex gap="2">
166+
<ActionTooltip title={i18n('header_forget')} placement={['left', 'auto']}>
167+
<div>
168+
<ButtonWithConfirmDialog
169+
buttonView="outlined"
170+
dialogHeader={i18n('header_forget')}
171+
dialogText={i18n('text_forget')}
172+
onConfirmAction={() =>
173+
forgetOperation({id, database})
174+
.unwrap()
175+
.then(() => {
176+
createToast({
177+
name: 'Forgotten',
178+
title: i18n('text_forgotten', {id}),
179+
type: 'success',
180+
});
181+
refreshTable();
182+
})
183+
}
184+
buttonDisabled={isLoadingCancel}
185+
>
186+
<Icon data={Ban} />
187+
</ButtonWithConfirmDialog>
188+
</div>
189+
</ActionTooltip>
190+
<ActionTooltip title={i18n('header_cancel')} placement={['right', 'auto']}>
191+
<div>
192+
<ButtonWithConfirmDialog
193+
buttonView="outlined"
194+
dialogHeader={i18n('header_cancel')}
195+
dialogText={i18n('text_cancel')}
196+
onConfirmAction={() =>
197+
cancelOperation({id, database})
198+
.unwrap()
199+
.then(() => {
200+
createToast({
201+
name: 'Cancelled',
202+
title: i18n('text_cancelled', {id}),
203+
type: 'success',
204+
});
205+
refreshTable();
206+
})
207+
}
208+
buttonDisabled={isForgetLoading}
209+
>
210+
<Icon data={CircleStop} />
211+
</ButtonWithConfirmDialog>
212+
</div>
213+
</ActionTooltip>
214+
</Flex>
215+
);
216+
}

src/containers/Operations/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {OperationKind} from '../../types/api/operationList';
1+
import type {OperationKind} from '../../types/api/operations';
22

33
import i18n from './i18n';
44

src/containers/Operations/i18n/en.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,12 @@
1313
"column_createTime": "Create Time",
1414
"column_endTime": "End Time",
1515
"column_duration": "Duration",
16-
"label_duration-ongoing": "{{value}} (ongoing)"
16+
"label_duration-ongoing": "{{value}} (ongoing)",
17+
18+
"header_cancel": "Cancel operation",
19+
"header_forget": "Forget operation",
20+
"text_cancel": "The operation will be cancelled. Do you want to proceed?",
21+
"text_forget": "The operation will be forgotten. Do you want to proceed?",
22+
"text_forgotten": "The operation {{id}} has been forgotten",
23+
"text_cancelled": "The operation {{id}} has been cancelled"
1724
}

0 commit comments

Comments
 (0)