Skip to content

Commit ed46a5f

Browse files
authored
fix: rework overview pane in schema browser (#954)
1 parent 6cb3e2b commit ed46a5f

File tree

8 files changed

+182
-144
lines changed

8 files changed

+182
-144
lines changed

.eslintrc

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"root": true,
88
"overrides": [
99
{
10-
"files": ["config-overrides.js", "commitlint.config.js"],
10+
"files": [
11+
"config-overrides.js",
12+
"commitlint.config.js"
13+
],
1114
"env": {
1215
"node": true,
1316
},
@@ -17,10 +20,20 @@
1720
"project": "./tsconfig.json",
1821
},
1922
"rules": {
20-
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
23+
"import/consistent-type-specifier-style": [
24+
"error",
25+
"prefer-top-level"
26+
],
2127
"@typescript-eslint/consistent-type-imports": [
2228
"error",
23-
{"prefer": "type-imports", "fixStyle": "separate-type-imports"},
29+
{
30+
"prefer": "type-imports",
31+
"fixStyle": "separate-type-imports"
32+
},
33+
],
34+
"curly": [
35+
"error",
36+
"all"
2437
],
2538
},
26-
}
39+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ yarn-debug.log*
2727
yarn-error.log*
2828

2929
.env
30+
31+
32+
embedded-ui.tar.bz2

src/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/components/InfoViewer/schemaOverview/index.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/containers/Tenant/ObjectSummary/ObjectSummary.tsx

Lines changed: 140 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,28 @@ import {StringParam, useQueryParam} from 'use-query-params';
1111
import {AsyncReplicationState} from '../../../components/AsyncReplicationState';
1212
import {ClipboardButton} from '../../../components/ClipboardButton';
1313
import InfoViewer from '../../../components/InfoViewer/InfoViewer';
14-
import {
15-
CDCStreamOverview,
16-
PersQueueGroupOverview,
17-
} from '../../../components/InfoViewer/schemaOverview';
14+
import type {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
15+
import {LinkWithIcon} from '../../../components/LinkWithIcon/LinkWithIcon';
1816
import {Loader} from '../../../components/Loader';
1917
import SplitPane from '../../../components/SplitPane';
20-
import routes, {createHref} from '../../../routes';
18+
import {getEntityName} from '../../../containers/Tenant/utils';
19+
import routes, {createExternalUILink, createHref} from '../../../routes';
2120
import {schemaApi, setShowPreview} from '../../../store/reducers/schema/schema';
2221
import {
2322
TENANT_PAGES_IDS,
2423
TENANT_QUERY_TABS_ID,
2524
TENANT_SUMMARY_TABS_IDS,
2625
} from '../../../store/reducers/tenant/constants';
2726
import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
28-
import type {EPathSubType} from '../../../types/api/schema';
29-
import {EPathType} from '../../../types/api/schema';
27+
import {EPathSubType, EPathType} from '../../../types/api/schema';
3028
import {cn} from '../../../utils/cn';
3129
import {
3230
DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
3331
DEFAULT_SIZE_TENANT_SUMMARY_KEY,
3432
} from '../../../utils/constants';
35-
import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
33+
import {formatDateTime, formatSecondsToHours} from '../../../utils/dataFormatters/dataFormatters';
3634
import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks';
3735
import {Acl} from '../Acl/Acl';
38-
import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource';
39-
import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable';
4036
import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
4137
import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
4238
import {TENANT_INFO_TABS, TENANT_SCHEMA_TAB, TenantTabsGroups} from '../TenantPages';
@@ -137,65 +133,148 @@ export function ObjectSummary({
137133
};
138134

139135
const renderObjectOverview = () => {
140-
const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
141-
const createdAt = startTimeInMilliseconds
142-
? formatDateTime(startTimeInMilliseconds)
143-
: 'unknown';
144-
const createdAtLabel = 'Created At';
145-
146-
// verbose mapping to guarantee a correct render for new path types
147-
// TS will error when a new type is added but not mapped here
148-
const pathTypeToComponent: Record<EPathType, (() => React.ReactNode) | undefined> = {
136+
if (!currentSchemaData) {
137+
return undefined;
138+
}
139+
const {CreateStep, PathType, PathSubType, PathId, PathVersion} = currentSchemaData;
140+
141+
const overview: InfoViewerItem[] = [];
142+
143+
overview.push({label: i18n('summary.type'), value: PathType?.replace(/^EPathType/, '')});
144+
145+
if (PathSubType !== EPathSubType.EPathSubTypeEmpty) {
146+
overview.push({
147+
label: i18n('summary.subtype'),
148+
value: PathSubType?.replace(/^EPathSubType/, ''),
149+
});
150+
}
151+
152+
overview.push({label: i18n('summary.id'), value: PathId});
153+
154+
overview.push({label: i18n('summary.version'), value: PathVersion});
155+
156+
overview.push({
157+
label: i18n('summary.created'),
158+
value: formatDateTime(CreateStep, ''),
159+
});
160+
161+
const {PathDescription} = currentObjectData;
162+
const title = getEntityName(PathDescription);
163+
164+
const getPathTypeOverview: Record<EPathType, (() => InfoViewerItem[]) | undefined> = {
149165
[EPathType.EPathTypeInvalid]: undefined,
150166
[EPathType.EPathTypeDir]: undefined,
151-
[EPathType.EPathTypeTable]: undefined,
167+
[EPathType.EPathTypeTable]: () => [
168+
{
169+
label: i18n('summary.partitions'),
170+
value: PathDescription?.TablePartitions?.length,
171+
},
172+
],
152173
[EPathType.EPathTypeSubDomain]: undefined,
153174
[EPathType.EPathTypeTableIndex]: undefined,
154-
[EPathType.EPathTypeExtSubDomain]: undefined,
155-
[EPathType.EPathTypeColumnStore]: undefined,
156-
[EPathType.EPathTypeColumnTable]: undefined,
157-
[EPathType.EPathTypeCdcStream]: () => <CDCStreamOverview data={currentObjectData} />,
158-
[EPathType.EPathTypePersQueueGroup]: () => (
159-
<PersQueueGroupOverview data={currentObjectData} />
160-
),
161-
[EPathType.EPathTypeExternalTable]: () => (
162-
<ExternalTableSummary data={currentObjectData} />
163-
),
164-
[EPathType.EPathTypeExternalDataSource]: () => (
165-
<ExternalDataSourceSummary data={currentObjectData} />
166-
),
175+
[EPathType.EPathTypeExtSubDomain]: () => [
176+
{
177+
label: i18n('summary.paths'),
178+
value: PathDescription?.DomainDescription?.PathsInside,
179+
},
180+
{
181+
label: i18n('summary.shards'),
182+
value: PathDescription?.DomainDescription?.ShardsInside,
183+
},
184+
],
185+
[EPathType.EPathTypeColumnStore]: () => [
186+
{
187+
label: i18n('summary.partitions'),
188+
value: PathDescription?.ColumnStoreDescription?.ColumnShards?.length,
189+
},
190+
],
191+
[EPathType.EPathTypeColumnTable]: () => [
192+
{
193+
label: i18n('summary.partitions'),
194+
value: PathDescription?.ColumnTableDescription?.Sharding?.ColumnShards?.length,
195+
},
196+
],
197+
[EPathType.EPathTypeCdcStream]: () => {
198+
const {Mode, Format} = PathDescription?.CdcStreamDescription || {};
199+
200+
return [
201+
{
202+
label: i18n('summary.mode'),
203+
value: Mode?.replace(/^ECdcStreamMode/, ''),
204+
},
205+
{
206+
label: i18n('summary.format'),
207+
value: Format?.replace(/^ECdcStreamFormat/, ''),
208+
},
209+
];
210+
},
211+
[EPathType.EPathTypePersQueueGroup]: () => {
212+
const pqGroup = PathDescription?.PersQueueGroup;
213+
const value = pqGroup?.PQTabletConfig?.PartitionConfig?.LifetimeSeconds;
214+
215+
return [
216+
{
217+
label: i18n('summary.partitions'),
218+
value: pqGroup?.Partitions?.length,
219+
},
220+
{
221+
label: i18n('summary.retention'),
222+
value: value && formatSecondsToHours(value),
223+
},
224+
];
225+
},
226+
[EPathType.EPathTypeExternalTable]: () => {
227+
const pathToDataSource = createExternalUILink({
228+
...queryParams,
229+
schema: PathDescription?.ExternalTableDescription?.DataSourcePath,
230+
});
231+
232+
const {SourceType, DataSourcePath} =
233+
PathDescription?.ExternalTableDescription || {};
234+
235+
const dataSourceName = DataSourcePath?.match(/([^/]*)\/*$/)?.[1] || '';
236+
237+
return [
238+
{label: i18n('summary.source-type'), value: SourceType},
239+
{
240+
label: i18n('summary.data-source'),
241+
value: DataSourcePath && (
242+
<span title={DataSourcePath}>
243+
<LinkWithIcon title={dataSourceName || ''} url={pathToDataSource} />
244+
</span>
245+
),
246+
},
247+
];
248+
},
249+
[EPathType.EPathTypeExternalDataSource]: () => [
250+
{
251+
label: i18n('summary.source-type'),
252+
value: PathDescription?.ExternalDataSourceDescription?.SourceType,
253+
},
254+
],
167255
[EPathType.EPathTypeView]: undefined,
168-
[EPathType.EPathTypeReplication]: () => (
169-
<InfoViewer
170-
info={[
171-
{
172-
label: createdAtLabel,
173-
value: createdAt,
174-
},
175-
{
176-
label: 'State',
177-
value: (
178-
<AsyncReplicationState
179-
state={
180-
currentObjectData?.PathDescription?.ReplicationDescription
181-
?.State
182-
}
183-
/>
184-
),
185-
},
186-
]}
187-
/>
188-
),
256+
[EPathType.EPathTypeReplication]: () => {
257+
const state = PathDescription?.ReplicationDescription?.State;
258+
259+
if (!state) {
260+
return [];
261+
}
262+
263+
return [
264+
{
265+
label: i18n('summary.state'),
266+
value: <AsyncReplicationState state={state} />,
267+
},
268+
];
269+
},
189270
};
190271

191-
let component =
192-
currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.();
193-
194-
if (!component) {
195-
component = <InfoViewer info={[{label: createdAtLabel, value: createdAt}]} />;
196-
}
272+
const pathTypeOverview = (PathType && getPathTypeOverview[PathType]?.()) || [];
273+
overview.push(...pathTypeOverview);
197274

198-
return component;
275+
// filter all empty values in according this requirement
276+
// https://github.com/ydb-platform/ydb-embedded-ui/issues/906
277+
return <InfoViewer title={title} info={overview.filter((i) => i.value)} />;
199278
};
200279

201280
const renderTabContent = () => {

src/containers/Tenant/i18n/en.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
{
22
"page.title": "Database",
3-
43
"pages.query": "Query",
54
"pages.diagnostics": "Diagnostics",
6-
75
"acl.owner": "Owner",
86
"acl.empty": "No Acl data",
9-
107
"summary.navigation": "Navigation",
118
"summary.showPreview": "Show preview",
9+
"summary.source-type": "Source Type",
10+
"summary.data-source": "Data Source",
1211
"summary.copySchemaPath": "Copy schema path",
13-
12+
"summary.type": "Type",
13+
"summary.subtype": "SubType",
14+
"summary.id": "Id",
15+
"summary.version": "Version",
16+
"summary.created": "Created",
17+
"summary.partitions": "Partitions count",
18+
"summary.paths": "Paths",
19+
"summary.shards": "Shards",
20+
"summary.state": "State",
21+
"summary.mode": "Mode",
22+
"summary.format": "Format",
23+
"summary.retention": "Retention",
1424
"actions.copied": "The path is copied to the clipboard",
1525
"actions.notCopied": "Couldn’t copy the path",
16-
1726
"actions.copyPath": "Copy path",
1827
"actions.openPreview": "Open preview",
1928
"actions.createTable": "Create table...",

0 commit comments

Comments
 (0)