Skip to content

Commit

Permalink
cluster-ui: v2 table page mock up
Browse files Browse the repository at this point in the history
This commit adds the base components for the v2 tables
overview page. The current route is under
`/v2/databases/:dbID`. We've also changed the v2 db page
to route to this page when clicking the link on the db
name column.

Epic: CRDB-37558
Fixes: #130675

Release note: None
  • Loading branch information
xinhaoz committed Sep 20, 2024
1 parent e08158e commit 2be4069
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 4 deletions.
20 changes: 20 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2024 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

export enum TableColName {
NAME = "Name",
REPLICATION_SIZE = "Replication Size",
RANGE_COUNT = "Ranges",
COLUMN_COUNT = "Columns",
NODE_REGIONS = "Regions / Nodes",
LIVE_DATA_PERCENTAGE = "% of Live data",
AUTO_STATS_COLLECTION = "Table auto stats collection",
STATS_LAST_UPDATED = "Stats last updated",
}
46 changes: 46 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import { Tabs } from "antd";
import React, { useState } from "react";

import { commonStyles } from "src/common";
import { PageLayout } from "src/layouts";
import { PageHeader } from "src/sharedFromCloud/pageHeader";

import { TablesPageV2 } from "./tablesView";

const { TabPane } = Tabs;

enum TabKeys {
TABLES = "tables",
GRANTS = "grants",
}
export const DatabaseDetailsPageV2 = () => {
const [currentTab, setCurrentTab] = useState(TabKeys.TABLES);

return (
<PageLayout>
<PageHeader title="myDB" />
<Tabs
defaultActiveKey={TabKeys.TABLES}
className={commonStyles("cockroach--tabs")}
onChange={setCurrentTab}
activeKey={currentTab}
destroyInactiveTabPane
>
<TabPane tab="Tables" key={TabKeys.TABLES}>
<TablesPageV2 />
</TabPane>
<TabPane tab="Grants" key={TabKeys.GRANTS}></TabPane>
</Tabs>
</PageLayout>
);
};
219 changes: 219 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Copyright 2024 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import { Badge, BadgeIntent } from "@cockroachlabs/ui-components";
import moment from "moment-timezone";
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Select, { OptionsType } from "react-select";

import { RegionNodesLabel } from "src/components/regionNodesLabel";
import { PageSection } from "src/layouts";
import { PageConfig, PageConfigItem } from "src/pageConfig";
import PageCount from "src/sharedFromCloud/pageCount";
import { Search } from "src/sharedFromCloud/search";
import { Table, TableColumnProps } from "src/sharedFromCloud/table";
import useTable from "src/sharedFromCloud/useTable";
import { ReactSelectOption } from "src/types/selectTypes";
import { Bytes, EncodeDatabaseTableUri } from "src/util";

import { TableColName } from "./constants";
import { TableRow } from "./types";

const mockRegionOptions = [
{ label: "US East (N. Virginia)", value: "us-east-1" },
{ label: "US East (Ohio)", value: "us-east-2" },
];

const mockLastUpdated = moment.utc();
const mockData: TableRow[] = new Array(20).fill(1).map((_, i) => ({
name: `myDB-${i}`,
qualifiedNameWithSchema: `public.table-${i}`,
dbName: `myDB-${i}`,
dbID: i,
replicationSizeBytes: i * 100,
rangeCount: i,
columnCount: i,
nodesByRegion:
i % 2 === 0
? {
[mockRegionOptions[0].value]: [1, 2],
[mockRegionOptions[1].value]: [3],
}
: null,
liveDataPercentage: 1,
liveDataBytes: i * 100,
totalDataBytes: i * 100,
autoStatsCollectionEnabled: i % 2 === 0,
statsLastUpdated: mockLastUpdated,
key: i.toString(),
}));

const filters = {};

const initialParams = {
filters,
pagination: {
page: 1,
pageSize: 10,
},
search: "",
sort: {
field: "name",
order: "asc" as const,
},
};

const columns: TableColumnProps<TableRow>[] = [
{
title: TableColName.NAME,
width: "15%",
sorter: true,
render: (t: TableRow) => {
// This linking is just temporary. We'll need to update it to the correct path
// using db ID and table ID once we have the table details page.
const encodedDBPath = EncodeDatabaseTableUri(t.dbName, t.name);
return <Link to={encodedDBPath}>{t.qualifiedNameWithSchema}</Link>;
},
},
{
title: TableColName.REPLICATION_SIZE,
width: "fit-content",
sorter: true,
render: (t: TableRow) => {
return Bytes(t.replicationSizeBytes);
},
},
{
title: TableColName.RANGE_COUNT,
width: "fit-content",
sorter: true,
render: (t: TableRow) => {
return t.rangeCount;
},
},
{
title: TableColName.COLUMN_COUNT,
width: "fit-content",
sorter: true,
render: (t: TableRow) => {
return t.columnCount;
},
},
{
title: TableColName.NODE_REGIONS,
width: "20%",
render: (t: TableRow) => (
<div>
{Object.entries(t.nodesByRegion ?? {}).map(([region, nodes]) => (
<RegionNodesLabel
key={region}
nodes={nodes}
region={{ label: region, code: region }}
/>
))}
</div>
),
},
{
title: TableColName.LIVE_DATA_PERCENTAGE,
sorter: true,
render: (t: TableRow) => {
return (
<div>
<div>{t.liveDataPercentage * 100}%</div>
<div>
{Bytes(t.liveDataBytes)} / {Bytes(t.totalDataBytes)}
</div>
</div>
);
},
},
{
title: TableColName.AUTO_STATS_COLLECTION,
sorter: true,
render: (t: TableRow) => {
let intent: BadgeIntent = "success";
let text = "Enabled";
if (!t.autoStatsCollectionEnabled) {
intent = "warning";
text = "Disabled";
}
return (
<Badge intent={intent} transformCase={"uppercase"}>
{text}
</Badge>
);
},
},
{
title: TableColName.STATS_LAST_UPDATED,
sorter: true,
render: (t: TableRow) => {
return t.statsLastUpdated.format("YYYY-MM-DD HH:mm:ss");
},
},
];

export const TablesPageV2 = () => {
const { params, setSearch } = useTable({
initial: initialParams,
});
const data = mockData;

const [nodeRegions, setNodeRegions] = useState<ReactSelectOption[]>([]);
const onNodeRegionsChange = (selected: OptionsType<ReactSelectOption>) => {
setNodeRegions((selected ?? []).map(v => v));
};

return (
<>
<PageSection>
<PageConfig>
<PageConfigItem>
<Search placeholder="Search tables" onSubmit={setSearch} />
</PageConfigItem>
<PageConfigItem minWidth={"200px"}>
<Select
placeholder={"Regions"}
name="nodeRegions"
options={mockRegionOptions}
clearable={true}
isMulti
value={nodeRegions}
onChange={onNodeRegionsChange}
/>
</PageConfigItem>
</PageConfig>
</PageSection>
<PageSection>
<PageCount
page={1}
pageSize={params.pagination.pageSize}
total={data.length}
entity="tables"
/>
<Table
columns={columns}
dataSource={data}
pagination={{
size: "small",
current: params.pagination.page,
pageSize: params.pagination.pageSize,
showSizeChanger: false,
position: ["bottomCenter"],
total: data.length,
}}
onChange={(_pagination, _sorter) => {}}
/>
</PageSection>
</>
);
};
28 changes: 28 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import { Moment } from "moment-timezone";

export type TableRow = {
qualifiedNameWithSchema: string;
name: string;
dbName: string;
dbID: number;
replicationSizeBytes: number;
rangeCount: number;
columnCount: number;
nodesByRegion: Record<string, number[]>;
liveDataPercentage: number;
liveDataBytes: number;
totalDataBytes: number;
autoStatsCollectionEnabled: boolean;
statsLastUpdated: Moment;
key: string;
};
6 changes: 2 additions & 4 deletions pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
} from "src/sharedFromCloud/table";
import useTable, { TableParams } from "src/sharedFromCloud/useTable";
import { ReactSelectOption } from "src/types/selectTypes";
import { Bytes, EncodeDatabaseUri } from "src/util";
import { Bytes } from "src/util";

import { DatabaseColName } from "./constants";
import { DatabaseRow } from "./databaseTypes";
Expand All @@ -52,9 +52,7 @@ const COLUMNS: TableColumnProps<DatabaseRow>[] = [
title: DatabaseColName.NAME,
sorter: true,
render: (db: DatabaseRow) => {
const encodedDBPath = EncodeDatabaseUri(db.name);
// TODO (xzhang: For CC we have to use `${location.pathname}/${db.name}`
return <Link to={encodedDBPath}>{db.name}</Link>;
return <Link to={`/v2/databases/${db.id}`}>{db.name}</Link>;
},
},
{
Expand Down
1 change: 1 addition & 0 deletions pkg/ui/workspaces/cluster-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export * from "./timestamp";
export * from "./databases";
export * from "./antdTheme";
export * from "./databasesV2";
export * from "./databaseDetailsV2";
// Reexport ConfigProvider instance from cluster-ui as exact instance
// required in Db Console to apply Antd theme in Db Console.
// TODO (koorosh): is it possible to define antd pacakge as peerDependency
Expand Down
1 change: 1 addition & 0 deletions pkg/ui/workspaces/cluster-ui/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const dashboardNameAttr = "dashboard_name";
export const databaseAttr = "database";
export const databaseNameAttr = "database_name";
export const databaseNameCCAttr = "databaseName";
export const databaseIDAttr = "dbID";
export const fingerprintIDAttr = "fingerprint_id";
export const implicitTxnAttr = "implicitTxn";
export const executionIdAttr = "execution_id";
Expand Down
7 changes: 7 additions & 0 deletions pkg/ui/workspaces/db-console/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
crlTheme,
ConfigProvider as ClusterUIConfigProvider,
DatabasesPageV2,
DatabaseDetailsPageV2,
} from "@cockroachlabs/cluster-ui";
import { ConfigProvider } from "antd";
import { ConnectedRouter } from "connected-react-router";
Expand All @@ -35,6 +36,7 @@ import {
dashboardNameAttr,
databaseAttr,
databaseNameAttr,
databaseIDAttr,
executionIdAttr,
implicitTxnAttr,
indexNameAttr,
Expand Down Expand Up @@ -211,6 +213,11 @@ export const App: React.FC<AppProps> = (props: AppProps) => {
path={"/v2/databases"}
component={DatabasesPageV2}
/>
<Route
exact
path={`/v2/databases/:${databaseIDAttr}`}
component={DatabaseDetailsPageV2}
/>
<Route
exact
path="/databases"
Expand Down
1 change: 1 addition & 0 deletions pkg/ui/workspaces/db-console/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const {
dashboardNameAttr,
databaseAttr,
databaseNameAttr,
databaseIDAttr,
fingerprintIDAttr,
executionIdAttr,
implicitTxnAttr,
Expand Down

0 comments on commit 2be4069

Please sign in to comment.