Skip to content

Commit 763fab2

Browse files
authored
refactor: tablet page (#1195)
1 parent f28402c commit 763fab2

File tree

10 files changed

+224
-204
lines changed

10 files changed

+224
-204
lines changed

src/containers/Tablet/Tablet.tsx

+134-143
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33
import {Flex, Tabs} from '@gravity-ui/uikit';
44
import {skipToken} from '@reduxjs/toolkit/query';
55
import {Helmet} from 'react-helmet-async';
6-
import {useLocation, useParams} from 'react-router-dom';
6+
import {useParams} from 'react-router-dom';
77
import {StringParam, useQueryParams} from 'use-query-params';
88
import {z} from 'zod';
99

@@ -13,12 +13,14 @@ import {ResponseError} from '../../components/Errors/ResponseError';
1313
import {InternalLink} from '../../components/InternalLink';
1414
import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper';
1515
import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
16-
import {getTabletPagePath, parseQuery} from '../../routes';
16+
import {getTabletPagePath} from '../../routes';
1717
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
1818
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
1919
import {tabletApi} from '../../store/reducers/tablet';
2020
import {EFlag} from '../../types/api/enums';
21-
import type {EType} from '../../types/api/tablet';
21+
import type {TTabletStateInfo} from '../../types/api/tablet';
22+
import {EType} from '../../types/api/tablet';
23+
import type {ITabletPreparedHistoryItem} from '../../types/store/tablet';
2224
import {cn} from '../../utils/cn';
2325
import {CLUSTER_DEFAULT_TITLE} from '../../utils/constants';
2426
import {useAutoRefreshInterval, useTypedDispatch, useTypedSelector} from '../../utils/hooks';
@@ -56,185 +58,174 @@ const TABLET_PAGE_TABS = [
5658
];
5759

5860
const tabletTabSchema = z.nativeEnum(TABLET_TABS_IDS).catch(TABLET_TABS_IDS.history);
61+
const eTypeSchema = z.nativeEnum(EType).or(z.undefined()).catch(undefined);
5962

60-
export const Tablet = () => {
63+
export function Tablet() {
6164
const dispatch = useTypedDispatch();
62-
const location = useLocation();
63-
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
6465

6566
const {id} = useParams<{id: string}>();
6667

67-
const [{activeTab}, setParams] = useQueryParams({
68-
activeTab: StringParam,
68+
const [
69+
{
70+
nodeId: queryNodeId,
71+
tenantName: queryTenantName,
72+
type: queryTabletType,
73+
clusterName: queryClusterName,
74+
},
75+
] = useQueryParams({
76+
nodeId: StringParam,
77+
tenantName: StringParam,
78+
type: StringParam,
79+
clusterName: StringParam,
6980
});
7081

71-
const tabletTab = tabletTabSchema.parse(activeTab);
72-
73-
const {
74-
nodeId: queryNodeId,
75-
tenantName: queryTenantName,
76-
type: queryTabletType,
77-
clusterName: queryClusterName,
78-
} = parseQuery(location);
79-
8082
const [autoRefreshInterval] = useAutoRefreshInterval();
81-
const {currentData, isFetching, error, refetch} = tabletApi.useGetTabletQuery(
83+
const {currentData, isFetching, error} = tabletApi.useGetTabletQuery(
8284
{id, database: queryTenantName?.toString()},
8385
{pollingInterval: autoRefreshInterval},
8486
);
8587

8688
const loading = isFetching && currentData === undefined;
87-
const {id: tabletId, data: tablet = {}, history = []} = currentData || {};
89+
const {data: tablet = {}, history = []} = currentData || {};
8890

8991
const {currentData: tenantPath} = tabletApi.useGetTabletDescribeQuery(
9092
tablet.TenantId ? {tenantId: tablet.TenantId} : skipToken,
9193
);
9294

93-
const hasHiveId = hasHive(tablet.HiveId);
95+
const nodeId = tablet.NodeId ?? queryNodeId;
96+
const tenantName = (tenantPath || queryTenantName) ?? undefined;
9497

95-
const noAdvancedInfo = !isUserAllowedToMakeChanges || !hasHiveId;
96-
97-
React.useEffect(() => {
98-
if (loading) {
99-
return;
100-
}
101-
if (noAdvancedInfo && TABLET_PAGE_TABS.find(({id}) => id === tabletTab)?.isAdvanced) {
102-
setParams({activeTab: TABLET_TABS_IDS.history});
103-
}
104-
}, [noAdvancedInfo, tabletTab, setParams, loading]);
105-
106-
const {
107-
currentData: advancedData,
108-
refetch: refetchAdvancedInfo,
109-
isFetching: isFetchingAdvancedData,
110-
} = tabletApi.useGetAdvancedTableInfoQuery(
111-
{id, hiveId: tablet.HiveId},
112-
{
113-
pollingInterval: autoRefreshInterval,
114-
skip: noAdvancedInfo || activeTab !== 'channels',
115-
},
116-
);
117-
118-
const refetchTabletInfo = React.useCallback(async () => {
119-
await Promise.all([refetch(), refetchAdvancedInfo()]);
120-
}, [refetch, refetchAdvancedInfo]);
121-
122-
const nodeId = tablet.NodeId?.toString() || queryNodeId?.toString();
123-
const tenantName = tenantPath || queryTenantName?.toString();
124-
125-
const type = tablet.Type || (queryTabletType?.toString() as EType | undefined);
98+
const tabletType = tablet.Type || eTypeSchema.parse(queryTabletType);
12699

127100
React.useEffect(() => {
128101
dispatch(
129102
setHeaderBreadcrumbs('tablet', {
130103
nodeIds: nodeId ? [nodeId] : [],
131104
tenantName,
132105
tabletId: id,
133-
tabletType: type,
106+
tabletType,
134107
}),
135108
);
136-
}, [dispatch, tenantName, id, nodeId, type]);
109+
}, [dispatch, tenantName, id, nodeId, tabletType]);
137110

138-
const renderPageMeta = () => {
139-
const {Leader, Type} = tablet;
140-
const database = tenantName ? `${i18n('tablet.meta-database')}: ${tenantName}` : undefined;
141-
const type = Type ? Type : undefined;
142-
const follower = Leader === false ? i18n('tablet.meta-follower').toUpperCase() : undefined;
111+
const {Leader, Type} = tablet;
112+
const database = tenantName ? `${i18n('tablet.meta-database')}: ${tenantName}` : undefined;
113+
const type = Type ? Type : undefined;
114+
const follower = Leader === false ? i18n('tablet.meta-follower').toUpperCase() : undefined;
143115

144-
return <PageMetaWithAutorefresh items={[database, type, follower]} />;
145-
};
146-
147-
const renderHelmet = () => {
148-
return (
116+
return (
117+
<Flex gap={5} direction="column" className={b()}>
149118
<Helmet>
150119
<title>{`${id}${i18n('tablet.header')}${
151120
tenantName || queryClusterName || CLUSTER_DEFAULT_TITLE
152121
}`}</title>
153122
</Helmet>
154-
);
155-
};
156-
157-
const renderTabs = () => {
158-
return (
159-
//block wrapper for tabs
160-
<div>
161-
<Tabs
162-
size="l"
163-
items={TABLET_PAGE_TABS.filter(({isAdvanced}) =>
164-
isAdvanced ? !noAdvancedInfo : true,
165-
)}
166-
activeTab={tabletTab}
167-
wrapTo={({id}, tabNode) => {
168-
const path = tabletId
169-
? getTabletPagePath(tabletId, {activeTab: id})
170-
: undefined;
171-
return (
172-
<InternalLink to={path} key={id}>
173-
{tabNode}
174-
</InternalLink>
175-
);
176-
}}
123+
<PageMetaWithAutorefresh items={[database, type, follower]} />
124+
<LoaderWrapper loading={loading} size="l">
125+
{error ? <ResponseError error={error} /> : null}
126+
{currentData ? <TabletContent id={id} tablet={tablet} history={history} /> : null}
127+
</LoaderWrapper>
128+
</Flex>
129+
);
130+
}
131+
132+
function TabletContent({
133+
id,
134+
tablet,
135+
history,
136+
}: {
137+
id: string;
138+
tablet: TTabletStateInfo;
139+
history: ITabletPreparedHistoryItem[];
140+
}) {
141+
const isEmpty = !Object.keys(tablet).length;
142+
const {Overall, HiveId} = tablet;
143+
144+
return (
145+
<EmptyStateWrapper
146+
title={i18n('emptyState')}
147+
className={b('placeholder')}
148+
isEmpty={isEmpty}
149+
>
150+
<Flex gap={5} direction="column">
151+
<EntityPageTitle
152+
entityName={i18n('tablet.header')}
153+
status={Overall ?? EFlag.Grey}
154+
id={id}
177155
/>
178-
</div>
179-
);
180-
};
181-
182-
const renderTabsContent = () => {
183-
switch (tabletTab) {
184-
case 'channels': {
185-
return (
186-
<LoaderWrapper
187-
loading={isFetchingAdvancedData && advancedData === undefined}
188-
size="l"
189-
>
190-
<TabletStorageInfo data={advancedData} />
191-
</LoaderWrapper>
192-
);
193-
}
194-
case 'history': {
195-
return <TabletTable history={history} />;
196-
}
197-
default:
198-
return null;
199-
}
200-
};
156+
<TabletControls tablet={tablet} />
157+
<TabletInfo tablet={tablet} />
158+
</Flex>
159+
<TabletTabs id={id} hiveId={HiveId} history={history} />
160+
</EmptyStateWrapper>
161+
);
162+
}
163+
164+
function TabletTabs({
165+
id,
166+
hiveId,
167+
history,
168+
}: {
169+
id: string;
170+
hiveId?: string;
171+
history: ITabletPreparedHistoryItem[];
172+
}) {
173+
const [{activeTab}, setParams] = useQueryParams({activeTab: StringParam});
174+
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
175+
176+
const noAdvancedInfo = !isUserAllowedToMakeChanges || !hasHive(hiveId);
201177

202-
const renderView = () => {
203-
if (error && !currentData) {
204-
return <ResponseError error={error} />;
178+
let tabletTab = tabletTabSchema.parse(activeTab);
179+
if (noAdvancedInfo && TABLET_PAGE_TABS.find((tab) => tab.id === tabletTab)?.isAdvanced) {
180+
tabletTab = TABLET_TABS_IDS.history;
181+
}
182+
183+
React.useEffect(() => {
184+
if (activeTab !== tabletTab) {
185+
setParams({activeTab: tabletTab}, 'replaceIn');
205186
}
187+
}, [activeTab, tabletTab, setParams]);
206188

207-
const {TabletId, Overall} = tablet;
208-
return (
209-
<React.Fragment>
210-
{error ? <ResponseError error={error} /> : null}
211-
<Flex gap={5} direction="column">
212-
<EntityPageTitle
213-
entityName={i18n('tablet.header')}
214-
status={Overall ?? EFlag.Grey}
215-
id={TabletId}
216-
/>
217-
<TabletControls tablet={tablet} fetchData={refetchTabletInfo} />
218-
<TabletInfo tablet={tablet} />
219-
</Flex>
220-
</React.Fragment>
221-
);
222-
};
223189
return (
224-
<Flex gap={5} direction="column" className={b()}>
225-
{renderHelmet()}
226-
{renderPageMeta()}
227-
<LoaderWrapper loading={loading} size="l">
228-
<EmptyStateWrapper
229-
title={i18n('emptyState')}
230-
className={b('placeholder')}
231-
isEmpty={!tablet || !Object.keys(tablet).length}
232-
>
233-
{renderView()}
234-
{renderTabs()}
235-
{renderTabsContent()}
236-
</EmptyStateWrapper>
237-
</LoaderWrapper>
190+
<Flex gap={5} direction="column">
191+
<Tabs
192+
size="l"
193+
items={TABLET_PAGE_TABS.filter(({isAdvanced}) =>
194+
isAdvanced ? !noAdvancedInfo : true,
195+
)}
196+
activeTab={tabletTab}
197+
wrapTo={(tab, tabNode) => {
198+
const path = getTabletPagePath(id, {activeTab: tab.id});
199+
return (
200+
<InternalLink to={path} key={tab.id}>
201+
{tabNode}
202+
</InternalLink>
203+
);
204+
}}
205+
/>
206+
{tabletTab === 'history' ? <TabletTable history={history} /> : null}
207+
{tabletTab === 'channels' && !noAdvancedInfo ? (
208+
<Channels id={id} hiveId={hiveId} />
209+
) : null}
238210
</Flex>
239211
);
240-
};
212+
}
213+
214+
function Channels({id, hiveId}: {id: string; hiveId: string}) {
215+
const [autoRefreshInterval] = useAutoRefreshInterval();
216+
const {currentData, error, isFetching} = tabletApi.useGetAdvancedTableInfoQuery(
217+
{id, hiveId},
218+
{
219+
pollingInterval: autoRefreshInterval,
220+
},
221+
);
222+
223+
const loading = isFetching && currentData === undefined;
224+
225+
return (
226+
<LoaderWrapper loading={loading} size="l">
227+
{error ? <ResponseError error={error} /> : null}
228+
{currentData ? <TabletStorageInfo data={currentData} /> : null}
229+
</LoaderWrapper>
230+
);
231+
}

0 commit comments

Comments
 (0)