Skip to content

Commit 2eec3d8

Browse files
committed
feat(Cluster): add Tablets tab
1 parent 8ef7604 commit 2eec3d8

File tree

4 files changed

+205
-168
lines changed

4 files changed

+205
-168
lines changed

src/containers/Cluster/Cluster.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
2626
import {parseVersionsToVersionToColorMap} from '../../utils/versions';
2727
import {NodesWrapper} from '../Nodes/NodesWrapper';
2828
import {StorageWrapper} from '../Storage/StorageWrapper';
29+
import {TabletsTable} from '../Tablets/TabletsTable';
2930
import {Tenants} from '../Tenants/Tenants';
3031
import {Versions} from '../Versions/Versions';
3132

@@ -153,6 +154,19 @@ export function Cluster({
153154
additionalClusterProps={additionalClusterProps}
154155
/>
155156
</Route>
157+
<Route
158+
path={
159+
getLocationObjectFromHref(getClusterPath(clusterTabsIds.tablets)).pathname
160+
}
161+
>
162+
<div className={b('tablets')}>
163+
<div className={b('fake-block')} />
164+
<TabletsTable
165+
tablets={cluster.SystemTablets ?? []}
166+
className={b('tablets-table')}
167+
/>
168+
</div>
169+
</Route>
156170
<Route
157171
path={
158172
getLocationObjectFromHref(getClusterPath(clusterTabsIds.tenants)).pathname

src/containers/Cluster/utils.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const clusterTabsIds = {
77
nodes: 'nodes',
88
storage: 'storage',
99
versions: 'versions',
10+
tablets: 'tablets',
1011
} as const;
1112

1213
export type ClusterTab = ValueOf<typeof clusterTabsIds>;
@@ -32,8 +33,12 @@ const versions = {
3233
id: clusterTabsIds.versions,
3334
title: 'Versions',
3435
};
36+
const tablets = {
37+
id: clusterTabsIds.tablets,
38+
title: 'Tablets',
39+
};
3540

36-
export const clusterTabs = [overview, tenants, nodes, storage, versions];
41+
export const clusterTabs = [overview, tenants, nodes, storage, tablets, versions];
3742

3843
export function isClusterTab(tab: any): tab is ClusterTab {
3944
return Object.values(clusterTabsIds).includes(tab);

src/containers/Tablets/Tablets.tsx

Lines changed: 2 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,174 +1,16 @@
1-
import {ArrowsRotateRight} from '@gravity-ui/icons';
2-
import type {Column as DataTableColumn} from '@gravity-ui/react-data-table';
3-
import {Icon, Text} from '@gravity-ui/uikit';
41
import {skipToken} from '@reduxjs/toolkit/query';
52

6-
import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
7-
import {DeveloperUILinkButton} from '../../components/DeveloperUILinkButton/DeveloperUILinkButton';
8-
import {EntityStatus} from '../../components/EntityStatus/EntityStatus';
93
import {ResponseError} from '../../components/Errors/ResponseError';
10-
import {InternalLink} from '../../components/InternalLink';
11-
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
124
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
13-
import {TabletState} from '../../components/TabletState/TabletState';
14-
import {getTabletPagePath} from '../../routes';
15-
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
16-
import {tabletApi} from '../../store/reducers/tablet';
175
import {selectTabletsWithFqdn, tabletsApi} from '../../store/reducers/tablets';
18-
import {ETabletState} from '../../types/api/tablet';
19-
import type {TTabletStateInfo} from '../../types/api/tablet';
206
import type {TabletsApiRequestParams} from '../../types/store/tablets';
217
import {cn} from '../../utils/cn';
22-
import {DEFAULT_TABLE_SETTINGS, EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
23-
import {calcUptime} from '../../utils/dataFormatters/dataFormatters';
24-
import {createTabletDeveloperUIHref} from '../../utils/developerUI/developerUI';
258
import {useAutoRefreshInterval, useTypedSelector} from '../../utils/hooks';
26-
import {getDefaultNodePath} from '../Node/NodePages';
279

28-
import i18n from './i18n';
10+
import {TabletsTable} from './TabletsTable';
2911

3012
const b = cn('tablets');
3113

32-
function getColumns({database}: {database?: string}) {
33-
const columns: DataTableColumn<TTabletStateInfo & {fqdn?: string}>[] = [
34-
{
35-
name: 'Type',
36-
get header() {
37-
return i18n('Type');
38-
},
39-
render: ({row}) => {
40-
const isFollower = row.Leader === false;
41-
return (
42-
<span>
43-
{row.Type} {isFollower ? <Text color="secondary">follower</Text> : ''}
44-
</span>
45-
);
46-
},
47-
},
48-
{
49-
name: 'TabletId',
50-
width: 220,
51-
get header() {
52-
return i18n('Tablet');
53-
},
54-
render: ({row}) => {
55-
if (!row.TabletId) {
56-
return EMPTY_DATA_PLACEHOLDER;
57-
}
58-
59-
const tabletPath = getTabletPagePath(row.TabletId, {
60-
nodeId: row.NodeId,
61-
type: row.Type,
62-
tenantName: database,
63-
});
64-
65-
return (
66-
<EntityStatus
67-
name={row.TabletId?.toString()}
68-
path={tabletPath}
69-
hasClipboardButton
70-
showStatus={false}
71-
additionalControls={
72-
<DeveloperUILinkButton
73-
href={createTabletDeveloperUIHref(row.TabletId)}
74-
/>
75-
}
76-
/>
77-
);
78-
},
79-
},
80-
{
81-
name: 'State',
82-
get header() {
83-
return i18n('State');
84-
},
85-
render: ({row}) => {
86-
return <TabletState state={row.State} />;
87-
},
88-
},
89-
{
90-
name: 'NodeId',
91-
get header() {
92-
return i18n('Node ID');
93-
},
94-
render: ({row}) => {
95-
const nodePath =
96-
row.NodeId === undefined ? undefined : getDefaultNodePath(row.NodeId);
97-
return <InternalLink to={nodePath}>{row.NodeId}</InternalLink>;
98-
},
99-
align: 'right',
100-
},
101-
{
102-
name: 'fqdn',
103-
get header() {
104-
return i18n('Node FQDN');
105-
},
106-
render: ({row}) => {
107-
if (!row.fqdn) {
108-
return <span></span>;
109-
}
110-
return <EntityStatus name={row.fqdn} showStatus={false} hasClipboardButton />;
111-
},
112-
},
113-
{
114-
name: 'Generation',
115-
get header() {
116-
return i18n('Generation');
117-
},
118-
align: 'right',
119-
},
120-
{
121-
name: 'Uptime',
122-
get header() {
123-
return i18n('Uptime');
124-
},
125-
render: ({row}) => {
126-
return calcUptime(row.ChangeTime);
127-
},
128-
sortAccessor: (row) => -Number(row.ChangeTime),
129-
align: 'right',
130-
},
131-
{
132-
name: 'Actions',
133-
sortable: false,
134-
resizeable: false,
135-
header: '',
136-
render: ({row}) => {
137-
return <TabletActions {...row} />;
138-
},
139-
},
140-
];
141-
return columns;
142-
}
143-
144-
function TabletActions(tablet: TTabletStateInfo) {
145-
const isDisabledRestart = tablet.State === ETabletState.Stopped;
146-
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
147-
const [killTablet] = tabletApi.useKillTabletMutation();
148-
149-
const id = tablet.TabletId;
150-
if (!id) {
151-
return null;
152-
}
153-
154-
return (
155-
<ButtonWithConfirmDialog
156-
buttonView="outlined"
157-
dialogHeader={i18n('dialog.kill-header')}
158-
dialogText={i18n('dialog.kill-text')}
159-
onConfirmAction={() => {
160-
return killTablet({id}).unwrap();
161-
}}
162-
buttonDisabled={isDisabledRestart || !isUserAllowedToMakeChanges}
163-
withPopover
164-
popoverContent={i18n('controls.kill-not-allowed')}
165-
popoverDisabled={isUserAllowedToMakeChanges}
166-
>
167-
<Icon data={ArrowsRotateRight} />
168-
</ButtonWithConfirmDialog>
169-
);
170-
}
171-
17214
interface TabletsProps {
17315
path?: string;
17416
database?: string;
@@ -203,14 +45,7 @@ export function Tablets({nodeId, path, database, className}: TabletsProps) {
20345
return (
20446
<div className={b(null, className)}>
20547
{error ? <ResponseError error={error} /> : null}
206-
{currentData ? (
207-
<ResizeableDataTable
208-
columns={getColumns({database})}
209-
data={tablets}
210-
settings={DEFAULT_TABLE_SETTINGS}
211-
emptyDataMessage={i18n('noTabletsData')}
212-
/>
213-
) : null}
48+
{currentData ? <TabletsTable tablets={tablets} database={database} /> : null}
21449
</div>
21550
);
21651
}

0 commit comments

Comments
 (0)