From 4314b2df39abf3f4a2854e29672de7fc322cf4ce Mon Sep 17 00:00:00 2001 From: Xin Hao Zhang Date: Tue, 15 Oct 2024 21:12:12 +0000 Subject: [PATCH 1/2] ui: regions label adjustments - Ensure the regions label text isn't condensed to 1 character per row by providing a min width. - Remove unnecessary widths on tables Epic: none Release note: None --- .../regionNodesLabel/components/regionLabel.module.scss | 4 ++++ .../components/regionNodesLabel/components/regionLabel.tsx | 4 +--- .../cluster-ui/src/components/tooltip/tooltip.module.scss | 2 +- .../cluster-ui/src/databaseDetailsV2/tablesView.tsx | 7 ------- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.module.scss b/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.module.scss index 68e7cad1ca18..4ed6fbd8705d 100644 --- a/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.module.scss @@ -8,6 +8,10 @@ .container { margin: 0.5rem; width: fit-content; + min-width: 100px; + &:hover { + cursor: pointer; + } } diff --git a/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.tsx b/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.tsx index 94bed1d26d5e..8d9016642f85 100644 --- a/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/components/regionNodesLabel/components/regionLabel.tsx @@ -31,9 +31,7 @@ export const RegionLabel: React.FC = ({
{region.label || "Unknown Region"} {showCode && ({region.code})} -
- -
+
diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tooltip/tooltip.module.scss b/pkg/ui/workspaces/cluster-ui/src/components/tooltip/tooltip.module.scss index 459f1a6f3cb2..350eb0b7d190 100644 --- a/pkg/ui/workspaces/cluster-ui/src/components/tooltip/tooltip.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/components/tooltip/tooltip.module.scss @@ -17,7 +17,7 @@ .title { a { - font-size: inherit; + font: inherit; color: inherit; } } diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx index a59da50e4d1b..c5f996538223 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx @@ -48,7 +48,6 @@ const COLUMNS: (TableColumnProps & { title: ( {TableColName.NAME} ), - width: "15%", sorter: (a, b) => a.tableName.localeCompare(b.tableName), render: (t: TableRow) => { return ( @@ -67,7 +66,6 @@ const COLUMNS: (TableColumnProps & { {TableColName.REPLICATION_SIZE} ), - width: "fit-content", sorter: (a, b) => a.replicationSizeBytes - b.replicationSizeBytes, render: (t: TableRow) => { return Bytes(t.replicationSizeBytes); @@ -80,7 +78,6 @@ const COLUMNS: (TableColumnProps & { {TableColName.RANGE_COUNT} ), - width: "fit-content", sorter: true, render: (t: TableRow) => { return t.rangeCount; @@ -89,7 +86,6 @@ const COLUMNS: (TableColumnProps & { }, { title: TableColName.COLUMN_COUNT, - width: "fit-content", sorter: true, render: (t: TableRow) => { return t.columnCount; @@ -98,7 +94,6 @@ const COLUMNS: (TableColumnProps & { }, { title: TableColName.INDEX_COUNT, - width: "fit-content", sorter: true, render: (t: TableRow) => { // We always include the primary index. @@ -113,7 +108,6 @@ const COLUMNS: (TableColumnProps & { ), hideIfTenant: true, - width: "20%", render: (t: TableRow) => ( ), @@ -129,7 +123,6 @@ const COLUMNS: (TableColumnProps & { ), sorter: true, - width: "fit-content", sortKey: TableSortOption.LIVE_DATA, render: (t: TableRow) => { return ( From af4cd32d656789f5afd82cd9bb166e625da942af Mon Sep 17 00:00:00 2001 From: Xin Hao Zhang Date: Wed, 16 Oct 2024 20:45:32 +0000 Subject: [PATCH 2/2] cluster-ui: misc v2 db pages styling adjustments Misc formatting - Fix page heading font styling - Fix search bar on focus border being cut off - Add `database` suffix to breadcrumb for db details page - Format live data percentage by adding `live data` and `total` as suffixes to numerator / denom respectively - Don't show db and table breadcrumb as not found when data is still being loaded - Put reset index action in row outside / on top of table - Add spinner state to refresh button when cached data is refreshing - Disable drop index action on cloud Table last updated/refreshed message & tooltip: - Align data last refreshed time with page count - Move full timestamp in table metadata tooltip - Better error message formatting - Show job progress when running Grants - Remove "Grants" header Epic: CRDB-37558 Closes: #132595 Release note (ui change): In the v2 database and db details pages, the refresh button tooltip will now include the cache refresh progress when the job is running as well as when the update started. --- .../cluster-ui/src/common/styles.module.scss | 2 - .../liveDataPercent/liveDataPercent.tsx | 28 +++ .../tableMetadataJobControl.spec.tsx | 5 +- .../tableMetadataJobControl.tsx | 49 +++-- .../tableMetadataJobProgress.module.scss | 8 + .../tableMetadataJobProgress.tsx | 40 ++++ .../tooltipMessages/index.tsx} | 6 +- ...ableMetadataLastUpdatedTooltip.module.scss | 8 + .../tableMetadataLastUpdatedTooltip.tsx | 90 ++++++++ .../src/databaseDetailsV2/dbGrantsView.tsx | 2 +- .../src/databaseDetailsV2/index.tsx | 20 +- .../src/databaseDetailsV2/tablesView.tsx | 56 +++-- .../cluster-ui/src/databasesV2/constants.ts | 2 +- .../cluster-ui/src/databasesV2/index.tsx | 53 +++-- .../sharedFromCloud/pageHeader.module.scss | 11 + .../src/sharedFromCloud/search.scss | 1 + .../src/summaryCard/summaryCard.module.scss | 2 +- .../cluster-ui/src/tableDetailsV2/index.tsx | 20 +- .../src/tableDetailsV2/tableGrantsView.tsx | 2 +- .../src/tableDetailsV2/tableIndexesView.tsx | 204 +++++++++--------- .../src/tableDetailsV2/tableOverview.tsx | 56 ++--- 21 files changed, 426 insertions(+), 239 deletions(-) create mode 100644 pkg/ui/workspaces/cluster-ui/src/components/liveDataPercent/liveDataPercent.tsx create mode 100644 pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.module.scss create mode 100644 pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.tsx rename pkg/ui/workspaces/cluster-ui/src/{constants/tooltipMessages.tsx => components/tooltipMessages/index.tsx} (74%) create mode 100644 pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.module.scss create mode 100644 pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.tsx diff --git a/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss b/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss index baf8110e043d..864f86ed805e 100644 --- a/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss @@ -13,8 +13,6 @@ :global(.crl-ant-tabs-tab) { font-family: $font-family--base; - font-size: 16px; - line-height: 1.5; letter-spacing: normal; color: $colors--neutral-7; } diff --git a/pkg/ui/workspaces/cluster-ui/src/components/liveDataPercent/liveDataPercent.tsx b/pkg/ui/workspaces/cluster-ui/src/components/liveDataPercent/liveDataPercent.tsx new file mode 100644 index 000000000000..0554ca04e3be --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/components/liveDataPercent/liveDataPercent.tsx @@ -0,0 +1,28 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. + +import { Row } from "antd"; +import React from "react"; + +import { Bytes, Percentage } from "src/util"; + +type Props = { + // Float between 0-1. + liveBytes: number; + totalBytes: number; +}; + +export const LiveDataPercent: React.FC = ({ liveBytes, totalBytes }) => { + return ( +
+ + {totalBytes ? Percentage(liveBytes, totalBytes, 1) : "0.0%"} + + + {Bytes(liveBytes)} live data / {Bytes(totalBytes)} total + +
+ ); +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.spec.tsx index 1cd299126019..5af4dfe03f65 100644 --- a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.spec.tsx @@ -48,7 +48,7 @@ describe("TableMetadataJobControl", () => { jest.clearAllMocks(); }); - it("renders the last refreshed time", () => { + it("renders the relative last refreshed time", () => { render( @@ -56,8 +56,9 @@ describe("TableMetadataJobControl", () => { ); expect(screen.getByText(/Last refreshed:/)).toBeInTheDocument(); + const lastCompletedRelativeTime = mockLastCompletedTime.fromNow(); expect( - screen.getByText(/Jan 01, 2024 at 12:00:00 UTC/), + screen.getByText(new RegExp(lastCompletedRelativeTime)), ).toBeInTheDocument(); }); diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.tsx b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.tsx index b99272bae8f9..170e7df71219 100644 --- a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobControl.tsx @@ -3,8 +3,8 @@ // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. -import { RedoOutlined } from "@ant-design/icons"; -import { Skeleton } from "antd"; +import { LoadingOutlined, RedoOutlined } from "@ant-design/icons"; +import { Skeleton, Spin } from "antd"; import React, { useCallback, useEffect } from "react"; import { @@ -12,15 +12,14 @@ import { triggerUpdateTableMetaJobApi, useTableMetaUpdateJob, } from "src/api/databases/tableMetaUpdateJobApi"; -import { TABLE_METADATA_LAST_UPDATED_HELP } from "src/constants/tooltipMessages"; import Button from "src/sharedFromCloud/button"; -import { Timestamp } from "src/timestamp"; -import { DATE_WITH_SECONDS_FORMAT_24_TZ } from "src/util"; import { usePrevious } from "src/util/hooks"; import { Tooltip } from "../tooltip"; +import { TableMetadataLastUpdatedTooltip } from "../tooltipMessages"; import styles from "./tableMetadataJobControl.module.scss"; +import { TableMetadataJobProgress } from "./tableMetadataJobProgress"; type TableMetadataJobControlProps = { // Callback for when the job has updated the metadata, i.e. the @@ -80,34 +79,40 @@ export const TableMetadataJobControl: React.FC< triggerUpdateTableMetaJob(false); }; - const durationText = jobStatus?.lastCompletedTime?.fromNow(); const isRunning = jobStatus?.currentStatus === TableMetadataJobStatus.RUNNING; - const refreshButtonTooltip = isRunning - ? "Data is currently refreshing" - : "Refresh data"; + const refreshButtonTooltip = isRunning ? ( + + ) : ( + "Refresh data" + ); return (
- - Last refreshed:{" "} - {" "} - {durationText && `(${durationText})`} - + + {durationText =>
Last refreshed: {durationText}
} +
- -
+ + <>
+
); diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.module.scss b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.module.scss new file mode 100644 index 000000000000..3b04daf4b078 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.module.scss @@ -0,0 +1,8 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. + +.progress-list { + list-style-position: inside; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.tsx b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.tsx new file mode 100644 index 000000000000..3824058bf39e --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/components/tableMetadataLastUpdated/tableMetadataJobProgress.tsx @@ -0,0 +1,40 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. + +import moment from "moment-timezone"; +import React from "react"; + +import { Timestamp } from "src/timestamp"; +import { DATE_WITH_SECONDS_FORMAT_24_TZ } from "src/util"; + +import styles from "./tableMetadataJobProgress.module.scss"; + +type Props = { + jobStartedTime: moment.Moment; + jobProgressFraction: number; // Between 0 and 1. +}; + +// This message is meant to be displayed when the job is running. +export const TableMetadataJobProgress: React.FC = ({ + jobStartedTime, + jobProgressFraction, +}) => { + const percentDone = Math.round(jobProgressFraction * 100); + return ( +
+ Refreshing data +
    +
  • {percentDone}% done
  • +
  • + Started at{" "} + +
  • +
+
+ ); +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/index.tsx similarity index 74% rename from pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx rename to pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/index.tsx index 7a2c6e7c1dd5..1f78f8fd6b36 100644 --- a/pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/index.tsx @@ -2,10 +2,11 @@ // // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. + import Link from "antd/es/typography/Link"; import React from "react"; -import { tableStatsClusterSetting } from "src/util"; +import { tableStatsClusterSetting } from "../../util"; export const AUTO_STATS_COLLECTION_HELP = ( @@ -17,5 +18,4 @@ export const AUTO_STATS_COLLECTION_HELP = ( ); -export const TABLE_METADATA_LAST_UPDATED_HELP = - "Data is last refreshed automatically (per cluster setting) or manually."; +export * from "./tableMetadataLastUpdatedTooltip"; diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.module.scss b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.module.scss new file mode 100644 index 000000000000..ad140d9eaaff --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.module.scss @@ -0,0 +1,8 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. + +.table-metadata-tooltip-content { + column-gap: 8px; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.tsx b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.tsx new file mode 100644 index 000000000000..f10b0e9008ec --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/components/tooltipMessages/tableMetadataLastUpdatedTooltip.tsx @@ -0,0 +1,90 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. +import { Icon } from "@cockroachlabs/ui-components"; +import { Row } from "antd"; +import moment from "moment-timezone"; +import React from "react"; + +import { Timestamp } from "../../timestamp"; +import { DATE_WITH_SECONDS_FORMAT_24_TZ } from "../../util"; +import { Tooltip } from "../tooltip"; + +import styles from "./tableMetadataLastUpdatedTooltip.module.scss"; + +const TABLE_METADATA_LAST_UPDATED_HELP = + "Data was last refreshed automatically (per cluster setting) or manually."; + +type Props = { + timestamp?: moment.Moment | null; + children: ( + formattedRelativeTime: string, + icon?: JSX.Element, + ) => React.ReactNode; + errorMessage?: string; +}; + +const formatErrorMessage = ( + errorMessage: string | null, + lastUpdatedTime: moment.Moment | null, +) => { + if (!errorMessage) { + return null; + } + + return ( + <> + Last refresh failed to retrieve data about this table. The data shown is + as of{" "} + + . +
+ Last refresh error: {errorMessage} + + ); +}; + +export const TableMetadataLastUpdatedTooltip = ({ + timestamp, + errorMessage, + children, +}: Props) => { + const durationText = timestamp?.fromNow() ?? "Never"; + const icon = errorMessage ? ( + + ) : ( + + ); + + const formattedErr = formatErrorMessage(errorMessage, timestamp); + return ( + + {formattedErr ?? ( + <> + {timestamp && ( + + )} +
+ {TABLE_METADATA_LAST_UPDATED_HELP} + + )} + + } + > + + {children(durationText, icon)} + +
+ ); +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/dbGrantsView.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/dbGrantsView.tsx index be5d1be06ad3..5d80314ecd38 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/dbGrantsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/dbGrantsView.tsx @@ -27,7 +27,7 @@ export const DbGrantsView: React.FC = () => { }, [databaseGrants]); return ( - + ); diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/index.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/index.tsx index 768ae342009c..ac646ecda5e5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/index.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/index.tsx @@ -26,7 +26,7 @@ enum TabKeys { export const DatabaseDetailsPageV2 = () => { const { dbID: dbIdRouteParam } = useRouteParams(); const dbId = parseInt(dbIdRouteParam, 10); - const { data, isLoading, error } = useDatabaseMetadataByID(dbId); + const { data, isLoading } = useDatabaseMetadataByID(dbId); const history = useHistory(); const location = useLocation(); const tab = queryByName(location, tabAttr) ?? TabKeys.TABLES; @@ -57,25 +57,23 @@ export const DatabaseDetailsPageV2 = () => { }, ]; - const dbName = - error?.status === 404 || !data - ? "Database Not Found" - : data.metadata.dbName; + const dbName = isLoading ? ( + + ) : ( + data?.metadata.dbName ?? "Database Not Found" + ); const breadCrumbItems = [ { name: "Databases", link: DB_PAGE_PATH }, { - name: dbName, - link: null, + name: <>Database: {dbName}, + link: "", }, ]; return ( - {dbName}} - /> + & { ), sorter: (a, b) => a.replicationSizeBytes - b.replicationSizeBytes, + align: "right", render: (t: TableRow) => { return Bytes(t.replicationSizeBytes); }, @@ -74,27 +76,38 @@ const COLUMNS: (TableColumnProps & { }, { title: ( - + {TableColName.RANGE_COUNT} ), sorter: true, + align: "right", render: (t: TableRow) => { return t.rangeCount; }, sortKey: TableSortOption.RANGES, }, { - title: TableColName.COLUMN_COUNT, + title: ( + + {TableColName.COLUMN_COUNT} + + ), sorter: true, + align: "right", render: (t: TableRow) => { return t.columnCount; }, sortKey: TableSortOption.COLUMNS, }, { - title: TableColName.INDEX_COUNT, + title: ( + + {TableColName.INDEX_COUNT} + + ), sorter: true, + align: "right", render: (t: TableRow) => { // We always include the primary index. return t.indexCount; @@ -108,6 +121,7 @@ const COLUMNS: (TableColumnProps & { ), hideIfTenant: true, + width: "fit-content", render: (t: TableRow) => ( ), @@ -123,15 +137,14 @@ const COLUMNS: (TableColumnProps & { ), sorter: true, + align: "right", sortKey: TableSortOption.LIVE_DATA, render: (t: TableRow) => { return ( -
-
{(t.percentLiveData * 100).toFixed(2)}%
-
- {Bytes(t.totalLiveDataBytes)} / {Bytes(t.totalDataBytes)} -
-
+ ); }, }, @@ -142,6 +155,7 @@ const COLUMNS: (TableColumnProps & { ), sorter: false, + align: "center", render: (t: TableRow) => { const type = t.autoStatsEnabled ? "success" : "default"; const text = t.autoStatsEnabled ? "ENABLED" : "DISABLED"; @@ -294,18 +308,18 @@ export const TablesPageV2 = () => {
- + + + + - } columns={colsWithSort} dataSource={tableData} pagination={{ diff --git a/pkg/ui/workspaces/cluster-ui/src/databasesV2/constants.ts b/pkg/ui/workspaces/cluster-ui/src/databasesV2/constants.ts index 46037df3b8ed..f119cde8fb72 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databasesV2/constants.ts +++ b/pkg/ui/workspaces/cluster-ui/src/databasesV2/constants.ts @@ -6,6 +6,6 @@ export enum DatabaseColName { NAME = "Name", SIZE = "Size", - TABLE_COUNT = "Table Count", + TABLE_COUNT = "Tables", NODE_REGIONS = "Regions / Nodes", } diff --git a/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx b/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx index 667ad4ffb10e..417f2d3270e2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx @@ -3,7 +3,7 @@ // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. -import { Skeleton } from "antd"; +import { Row, Skeleton } from "antd"; import React, { useContext, useMemo } from "react"; import { Link } from "react-router-dom"; @@ -18,6 +18,8 @@ import { NodeRegionsSelector } from "src/components/nodeRegionsSelector/nodeRegi import { RegionNodesLabel } from "src/components/regionNodesLabel"; import { TableMetadataJobControl } from "src/components/tableMetadataLastUpdated/tableMetadataJobControl"; import { Tooltip } from "src/components/tooltip"; +import { AUTO_STATS_COLLECTION_HELP } from "src/components/tooltipMessages"; +import { ClusterDetailsContext } from "src/contexts"; import { PageLayout, PageSection } from "src/layouts"; import { PageConfig, PageConfigItem } from "src/pageConfig"; import { BooleanSetting } from "src/settings"; @@ -34,9 +36,6 @@ import useTable, { TableParams } from "src/sharedFromCloud/useTable"; import { StoreID } from "src/types/clusterTypes"; import { Bytes } from "src/util"; -import { AUTO_STATS_COLLECTION_HELP } from "../constants/tooltipMessages"; -import { ClusterDetailsContext } from "../contexts"; - import { DatabaseColName } from "./constants"; import { DatabaseRow } from "./databaseTypes"; import { rawDatabaseMetadataToDatabaseRows } from "./utils"; @@ -71,6 +70,7 @@ const COLUMNS: (TableColumnProps & { ), sortKey: DatabaseSortOptions.REPLICATION_SIZE, sorter: (a, b) => a.approximateDiskSizeBytes - b.approximateDiskSizeBytes, + align: "right", render: (db: DatabaseRow) => { return Bytes(db.approximateDiskSizeBytes); }, @@ -83,6 +83,7 @@ const COLUMNS: (TableColumnProps & { ), sortKey: DatabaseSortOptions.TABLE_COUNT, sorter: true, + align: "right", render: (db: DatabaseRow) => { return db.tableCount; }, @@ -213,34 +214,32 @@ export const DatabasesPageV2 = () => { } /> - - - - + + + + + {!isTenant && ( + + - {!isTenant && ( - - - - )} - - + )} + - + + + +
- } columns={colsWithSort} dataSource={tableData} pagination={{ diff --git a/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/pageHeader.module.scss b/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/pageHeader.module.scss index 948bb95cbbc1..73eecd40445e 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/pageHeader.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/pageHeader.module.scss @@ -3,6 +3,9 @@ // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. +@import "src/core/index.module"; +@import "application.module.scss"; + .page-header { width: 100%; margin-bottom: crl-gutters(2); @@ -18,8 +21,15 @@ align-items: center; // Allows the text to center vertically. margin-bottom: 0; gap: crl-gutters(2); + color: $colors--neutral-7; + font-family: $font-family--semi-bold; + font-style: normal; + font-stretch: normal; + font-size: $font-size--large; + padding-bottom: 12px; } + .header-breadcrumbs { margin-top: crl-gutters(1); margin-bottom: crl-gutters(1); @@ -47,3 +57,4 @@ .loader { margin-left: crl-gutters(1); } + diff --git a/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/search.scss b/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/search.scss index 58a8aa7f44d4..e4fc2e6767b2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/search.scss +++ b/pkg/ui/workspaces/cluster-ui/src/sharedFromCloud/search.scss @@ -7,6 +7,7 @@ .search { width: 280px; + margin: 2px; &__button { background-color: transparent; diff --git a/pkg/ui/workspaces/cluster-ui/src/summaryCard/summaryCard.module.scss b/pkg/ui/workspaces/cluster-ui/src/summaryCard/summaryCard.module.scss index b9f621848b2d..5acf1be084d8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/summaryCard/summaryCard.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/summaryCard/summaryCard.module.scss @@ -75,7 +75,7 @@ &--value { font-family: $font-family--base; font-size: 14px; - font-weight: $font-weight--bold; + font-weight: $font-weight--medium; line-height: 1.71; letter-spacing: 0.1px; color: $popover-color; diff --git a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/index.tsx b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/index.tsx index 5d1590036469..524965e444b8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/index.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/index.tsx @@ -3,7 +3,7 @@ // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. -import { Tabs } from "antd"; +import { Skeleton, Tabs } from "antd"; import React, { useState } from "react"; import { useTableDetails } from "src/api/databases/getTableMetadataApi"; @@ -29,20 +29,20 @@ export const TableDetailsPageV2 = () => { const { data, error, isLoading } = useTableDetails({ tableId: parseInt(tableID, 10), }); - // The table name is undefined if the table does not exist. - const tableNotFound = error?.status === 404; - const partiallyQualifiedTableName = !tableNotFound - ? data - ? data.metadata.schemaName + "." + data.metadata.tableName - : "" - : "Table not found"; + const partiallyQualifiedTableName = isLoading ? ( + + ) : data ? ( + data.metadata.schemaName + "." + data.metadata.tableName + ) : ( + "Table not found" + ); const breadCrumbItems = [ { link: `/databases`, name: "Databases" }, { - link: `/databases/${data?.metadata.dbId}`, - name: data?.metadata.dbName, + link: data ? `/databases/${data?.metadata.dbId}` : "", + name: `Database: ${data?.metadata?.dbName ?? ""}`, }, { link: null, diff --git a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableGrantsView.tsx b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableGrantsView.tsx index 5b1eefd81cc1..35a0f8262691 100644 --- a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableGrantsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableGrantsView.tsx @@ -27,7 +27,7 @@ export const TableGrantsView: React.FC = () => { }, [tableGrants]); return ( - + ); diff --git a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableIndexesView.tsx b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableIndexesView.tsx index 56736b536e09..81345b6f01e6 100644 --- a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableIndexesView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableIndexesView.tsx @@ -4,7 +4,7 @@ // included in the /LICENSE file. import { Row, Tag } from "antd"; -import React from "react"; +import React, { useContext } from "react"; import { resetIndexStatsApi, @@ -19,90 +19,94 @@ import { Table, TableColumnProps } from "src/sharedFromCloud/table"; import { Timestamp } from "src/timestamp"; import { DATE_WITH_SECONDS_FORMAT_24_TZ } from "src/util"; +import { CockroachCloudContext } from "../contexts"; + type TableIndexRow = TableIndex & { key: React.Key; }; -const COLUMNS: TableColumnProps[] = [ - { - title: "Index Name", - render: (idx: TableIndexRow) => ( - - ), - }, - { - title: "Last Read", - render: (idx: TableIndexRow) => ( - - ), - }, - { - title: "Total Reads", - sorter: true, - render: (idx: TableIndexRow) => idx.totalReads, - }, - { - title: ( - & { hideIfCloud?: boolean })[] = + [ + { + title: "Index Name", + render: (idx: TableIndexRow) => ( + + ), + }, + { + title: "Last Read", + render: (idx: TableIndexRow) => ( + + ), + }, + { + title: "Total Reads", + sorter: true, + render: (idx: TableIndexRow) => idx.totalReads, + }, + { + title: ( + - Recommendations - - ), - sorter: true, - render: (idx: TableIndexRow) => { - if (idx.indexRecs.length === 0 || idx.indexType === "primary") { - return "None"; - } - const recs = idx.indexRecs.map((rec, i) => { - // The only rec right now is "DROP_UNUSED". - return ( - - Drop unused index - - ); - }); - return {recs}; + > + Recommendations + + ), + sorter: true, + render: (idx: TableIndexRow) => { + if (idx.indexRecs.length === 0 || idx.indexType === "primary") { + return "None"; + } + const recs = idx.indexRecs.map((rec, i) => { + // The only rec right now is "DROP_UNUSED". + return ( + + Drop unused index + + ); + }); + return {recs}; + }, }, - }, - { - title: "Action", - sorter: false, - render: (idx: TableIndexRow) => { - if (idx.indexRecs.length === 0 || idx.indexType === "primary") { - return null; - } + { + title: "Action", + sorter: false, + hideIfCloud: true, + render: (idx: TableIndexRow) => { + if (idx.indexRecs.length === 0 || idx.indexType === "primary") { + return null; + } - const stat = { - indexName: idx.indexName, - indexRecommendations: idx.indexRecs.map(rec => ({ - type: "DROP_UNUSED" as const, - reason: rec.reason, - })), - }; - // The action button expects an escaped schema qualified table name. - return ( - - ); + const stat = { + indexName: idx.indexName, + indexRecommendations: idx.indexRecs.map(rec => ({ + type: "DROP_UNUSED" as const, + reason: rec.reason, + })), + }; + // The action button expects an escaped schema qualified table name. + return ( + + ); + }, }, - }, -]; + ]; type Props = { dbName: string; @@ -120,6 +124,7 @@ export const TableIndexesView: React.FC = ({ tableName: tableName, schemaName: schemaName, }); + const isCloud = useContext(CockroachCloudContext); const tableIndexRows = indexStats.tableIndexes.map((idx, i) => ({ ...idx, @@ -131,34 +136,35 @@ export const TableIndexesView: React.FC = ({ return refreshIndexStats(); }; - const lastResetAction = ( - - !isCloud || !col.hideIfCloud); + + return ( +
+ + - Last reset:{" "} - + Last reset:{" "} + + +
+
+ ); }; diff --git a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableOverview.tsx b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableOverview.tsx index b98978fe741f..ee719485f83f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableOverview.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/tableDetailsV2/tableOverview.tsx @@ -3,15 +3,13 @@ // Use of this software is governed by the CockroachDB Software License // included in the /LICENSE file. -import { Icon } from "@cockroachlabs/ui-components"; import { Col, Row, Skeleton } from "antd"; -import moment from "moment-timezone"; import React, { useContext } from "react"; import { useNodeStatuses } from "src/api"; import { TableDetails } from "src/api/databases/getTableMetadataApi"; -import { Tooltip } from "src/components/tooltip"; -import { TABLE_METADATA_LAST_UPDATED_HELP } from "src/constants/tooltipMessages"; +import { LiveDataPercent } from "src/components/liveDataPercent/liveDataPercent"; +import { TableMetadataLastUpdatedTooltip } from "src/components/tooltipMessages/tableMetadataLastUpdatedTooltip"; import { ClusterDetailsContext } from "src/contexts"; import { PageSection } from "src/layouts"; import { SqlBox, SqlBoxSize } from "src/sql"; @@ -55,14 +53,6 @@ export const TableOverview: React.FC = ({ .join(", "); }; - const percentLiveDataWithPrecision = (metadata.percentLiveData * 100).toFixed( - 2, - ); - - const formattedErrorText = metadata.lastUpdateError - ? "Update error: " + metadata.lastUpdateError - : null; - return ( <> @@ -71,28 +61,21 @@ export const TableOverview: React.FC = ({ - - - {metadata.lastUpdateError ? ( - - ) : ( - - )} - - {" "} - Last updated:{" "} - - - - + {(durationText, icon) => ( + <> + {icon} + Last updated: {durationText} + + )} + + + @@ -119,13 +102,10 @@ export const TableOverview: React.FC = ({ -
{percentLiveDataWithPrecision}%
-
- {Bytes(metadata.totalLiveDataBytes)} /{" "} - {Bytes(metadata.totalLiveDataBytes)} -
- + } />